From 7dd205535bb1c641e851599dd1b45a469811c6e9 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Thu, 12 May 2022 22:26:59 +1000 Subject: [PATCH] attach exec via SDK --- go.mod | 5 +- go.sum | 3 + pkg/commands/attaching.go | 46 ++ pkg/commands/streamer/common.go | 15 + pkg/commands/streamer/in.go | 44 ++ pkg/commands/streamer/out.go | 40 + pkg/commands/streamer/streamer.go | 185 +++++ pkg/commands/streamer/streamer_unix.go | 22 + pkg/commands/streamer/streamer_windows.go | 21 + pkg/gui/containers_panel.go | 30 +- vendor/github.com/Azure/go-ansiterm/LICENSE | 21 + vendor/github.com/Azure/go-ansiterm/README.md | 12 + .../github.com/Azure/go-ansiterm/constants.go | 188 +++++ .../github.com/Azure/go-ansiterm/context.go | 7 + .../Azure/go-ansiterm/csi_entry_state.go | 49 ++ .../Azure/go-ansiterm/csi_param_state.go | 38 + .../go-ansiterm/escape_intermediate_state.go | 36 + .../Azure/go-ansiterm/escape_state.go | 47 ++ .../Azure/go-ansiterm/event_handler.go | 90 +++ .../Azure/go-ansiterm/ground_state.go | 24 + .../Azure/go-ansiterm/osc_string_state.go | 31 + vendor/github.com/Azure/go-ansiterm/parser.go | 151 ++++ .../go-ansiterm/parser_action_helpers.go | 99 +++ .../Azure/go-ansiterm/parser_actions.go | 119 +++ vendor/github.com/Azure/go-ansiterm/states.go | 71 ++ .../github.com/Azure/go-ansiterm/utilities.go | 21 + .../Azure/go-ansiterm/winterm/ansi.go | 196 +++++ .../Azure/go-ansiterm/winterm/api.go | 327 ++++++++ .../go-ansiterm/winterm/attr_translation.go | 100 +++ .../go-ansiterm/winterm/cursor_helpers.go | 101 +++ .../go-ansiterm/winterm/erase_helpers.go | 84 ++ .../go-ansiterm/winterm/scroll_helper.go | 118 +++ .../Azure/go-ansiterm/winterm/utilities.go | 9 + .../go-ansiterm/winterm/win_event_handler.go | 743 ++++++++++++++++++ .../docker/docker/pkg/term/deprecated.go | 84 ++ .../docker/docker/pkg/term/deprecated_unix.go | 21 + .../github.com/micmonay/keybd_event/LICENSE | 9 + .../github.com/micmonay/keybd_event/README.md | 77 ++ .../micmonay/keybd_event/keybd_darwin.go | 262 ++++++ .../micmonay/keybd_event/keybd_event.go | 86 ++ .../micmonay/keybd_event/keybd_linux.go | 549 +++++++++++++ .../micmonay/keybd_event/keybd_windows.go | 310 ++++++++ .../micmonay/keybd_event/keyboard.png | Bin 0 -> 40492 bytes vendor/github.com/moby/term/.gitignore | 8 + vendor/github.com/moby/term/LICENSE | 191 +++++ vendor/github.com/moby/term/README.md | 36 + vendor/github.com/moby/term/ascii.go | 66 ++ vendor/github.com/moby/term/proxy.go | 88 +++ vendor/github.com/moby/term/tc.go | 19 + vendor/github.com/moby/term/term.go | 120 +++ vendor/github.com/moby/term/term_windows.go | 231 ++++++ vendor/github.com/moby/term/termios.go | 35 + vendor/github.com/moby/term/termios_bsd.go | 12 + vendor/github.com/moby/term/termios_nonbsd.go | 12 + .../moby/term/windows/ansi_reader.go | 252 ++++++ .../moby/term/windows/ansi_writer.go | 56 ++ .../github.com/moby/term/windows/console.go | 39 + vendor/github.com/moby/term/windows/doc.go | 5 + vendor/github.com/moby/term/winsize.go | 20 + vendor/modules.txt | 12 + 60 files changed, 5683 insertions(+), 10 deletions(-) create mode 100644 pkg/commands/attaching.go create mode 100644 pkg/commands/streamer/common.go create mode 100644 pkg/commands/streamer/in.go create mode 100644 pkg/commands/streamer/out.go create mode 100644 pkg/commands/streamer/streamer.go create mode 100644 pkg/commands/streamer/streamer_unix.go create mode 100644 pkg/commands/streamer/streamer_windows.go create mode 100644 vendor/github.com/Azure/go-ansiterm/LICENSE create mode 100644 vendor/github.com/Azure/go-ansiterm/README.md create mode 100644 vendor/github.com/Azure/go-ansiterm/constants.go create mode 100644 vendor/github.com/Azure/go-ansiterm/context.go create mode 100644 vendor/github.com/Azure/go-ansiterm/csi_entry_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/csi_param_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/escape_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/event_handler.go create mode 100644 vendor/github.com/Azure/go-ansiterm/ground_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/osc_string_state.go create mode 100644 vendor/github.com/Azure/go-ansiterm/parser.go create mode 100644 vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go create mode 100644 vendor/github.com/Azure/go-ansiterm/parser_actions.go create mode 100644 vendor/github.com/Azure/go-ansiterm/states.go create mode 100644 vendor/github.com/Azure/go-ansiterm/utilities.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/ansi.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/api.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/utilities.go create mode 100644 vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go create mode 100644 vendor/github.com/docker/docker/pkg/term/deprecated.go create mode 100644 vendor/github.com/docker/docker/pkg/term/deprecated_unix.go create mode 100644 vendor/github.com/micmonay/keybd_event/LICENSE create mode 100644 vendor/github.com/micmonay/keybd_event/README.md create mode 100644 vendor/github.com/micmonay/keybd_event/keybd_darwin.go create mode 100644 vendor/github.com/micmonay/keybd_event/keybd_event.go create mode 100644 vendor/github.com/micmonay/keybd_event/keybd_linux.go create mode 100644 vendor/github.com/micmonay/keybd_event/keybd_windows.go create mode 100644 vendor/github.com/micmonay/keybd_event/keyboard.png create mode 100644 vendor/github.com/moby/term/.gitignore create mode 100644 vendor/github.com/moby/term/LICENSE create mode 100644 vendor/github.com/moby/term/README.md create mode 100644 vendor/github.com/moby/term/ascii.go create mode 100644 vendor/github.com/moby/term/proxy.go create mode 100644 vendor/github.com/moby/term/tc.go create mode 100644 vendor/github.com/moby/term/term.go create mode 100644 vendor/github.com/moby/term/term_windows.go create mode 100644 vendor/github.com/moby/term/termios.go create mode 100644 vendor/github.com/moby/term/termios_bsd.go create mode 100644 vendor/github.com/moby/term/termios_nonbsd.go create mode 100644 vendor/github.com/moby/term/windows/ansi_reader.go create mode 100644 vendor/github.com/moby/term/windows/ansi_writer.go create mode 100644 vendor/github.com/moby/term/windows/console.go create mode 100644 vendor/github.com/moby/term/windows/doc.go create mode 100644 vendor/github.com/moby/term/winsize.go diff --git a/go.mod b/go.mod index b0abab8b..bb0173c7 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,8 @@ require ( github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56 github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 github.com/mgutz/str v1.2.0 + github.com/micmonay/keybd_event v1.1.1 + github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/samber/lo v1.20.0 github.com/sirupsen/logrus v1.4.2 github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad @@ -26,6 +28,7 @@ require ( ) require ( + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.4.14 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect @@ -34,12 +37,12 @@ require ( github.com/gdamore/encoding v1.0.0 // indirect github.com/gdamore/tcell/v2 v2.5.1 // indirect github.com/gogo/protobuf v1.3.1 // indirect + github.com/google/go-cmp v0.5.5 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.11 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect github.com/onsi/ginkgo v1.8.0 // indirect github.com/onsi/gomega v1.5.0 // indirect diff --git a/go.sum b/go.sum index 9830fcb4..22da7c29 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,7 @@ github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1 h1:1fx+RA5lk1ZkzPA github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1/go.mod h1:z0nyIb42Zs97wyX1V+8MbEFhHeTw1OgFQfR6q57ZuHc= github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do= github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -75,6 +76,8 @@ github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 h1:BrhJNdEFWGui github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767/go.mod h1:ct+byCpkFokm4J0tiuAvB8cf2ttm6GcCe89Yr25nGKg= github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw= github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w= +github.com/micmonay/keybd_event v1.1.1 h1:rv7omwXWYL9Lgf3PUq6uBgJI2k1yGkL/GD6dxc6nmSs= +github.com/micmonay/keybd_event v1.1.1/go.mod h1:CGMWMDNgsfPljzrAWoybUOSKafQPZpv+rLigt2LzNGI= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= diff --git a/pkg/commands/attaching.go b/pkg/commands/attaching.go new file mode 100644 index 00000000..f03dec92 --- /dev/null +++ b/pkg/commands/attaching.go @@ -0,0 +1,46 @@ +package commands + +import ( + "context" + + "github.com/docker/docker/api/types" + "github.com/jesseduffield/lazydocker/pkg/commands/streamer" +) + +// AttachExecContainer attach container +func (c *DockerCommand) AttachExecContainer(id string, cmd []string) error { + exec, err := c.createExec(id, cmd) + if err != nil { + return err + } + + ctx := context.TODO() + + resp, err := c.Client.ContainerExecAttach(ctx, exec.ID, types.ExecStartCheck{Tty: true}) + if err != nil { + return err + } + defer resp.Close() + + f := func(ctx context.Context, id string, options types.ResizeOptions) error { + return c.Client.ContainerExecResize(ctx, id, options) + } + + s := streamer.New(c.Log) + if err := s.Stream(ctx, exec.ID, resp, streamer.ResizeContainer(f)); err != nil { + return err + } + + return nil +} + +// createExec container exec create +func (c *DockerCommand) createExec(containerId string, cmd []string) (types.IDResponse, error) { + return c.Client.ContainerExecCreate(context.TODO(), containerId, types.ExecConfig{ + Tty: true, + AttachStdin: true, + AttachStderr: true, + AttachStdout: true, + Cmd: cmd, + }) +} diff --git a/pkg/commands/streamer/common.go b/pkg/commands/streamer/common.go new file mode 100644 index 00000000..d9516995 --- /dev/null +++ b/pkg/commands/streamer/common.go @@ -0,0 +1,15 @@ +package streamer + +import "github.com/moby/term" + +type CommonStream struct { + Fd uintptr + IsTerminal bool + State *term.State +} + +func (s *CommonStream) RestoreTerminal() { + if s.State != nil { + term.RestoreTerminal(s.Fd, s.State) + } +} diff --git a/pkg/commands/streamer/in.go b/pkg/commands/streamer/in.go new file mode 100644 index 00000000..8c94a668 --- /dev/null +++ b/pkg/commands/streamer/in.go @@ -0,0 +1,44 @@ +package streamer + +import ( + "io" + + "github.com/moby/term" +) + +type In struct { + CommonStream + in io.ReadCloser +} + +func (i *In) Read(p []byte) (int, error) { + return i.in.Read(p) +} + +func (i *In) Close() error { + return i.in.Close() +} + +func (i *In) SetRawTerminal() error { + var err error + i.CommonStream.State, err = term.SetRawTerminal(i.Fd) + return err +} + +func (i *In) RestoreTerminal() error { + if i.CommonStream.State == nil { + return nil + } + return term.RestoreTerminal(i.CommonStream.Fd, i.CommonStream.State) +} + +func NewIn(in io.ReadCloser) *In { + fd, isTerminal := term.GetFdInfo(in) + return &In{ + in: in, + CommonStream: CommonStream{ + Fd: fd, + IsTerminal: isTerminal, + }, + } +} diff --git a/pkg/commands/streamer/out.go b/pkg/commands/streamer/out.go new file mode 100644 index 00000000..4a6d745d --- /dev/null +++ b/pkg/commands/streamer/out.go @@ -0,0 +1,40 @@ +package streamer + +import ( + "io" + "log" + + "github.com/moby/term" +) + +type Out struct { + CommonStream + out io.Writer +} + +func (o *Out) Write(p []byte) (int, error) { + return o.out.Write(p) +} + +func (o *Out) GetTtySize() (uint, uint) { + if !o.IsTerminal { + return 0, 0 + } + ws, err := term.GetWinsize(o.Fd) + if err != nil { + log.Printf("getting tty size: %s\n", err) + return 0, 0 + } + return uint(ws.Height), uint(ws.Width) +} + +func NewOut(out io.Writer) *Out { + fd, isTerminal := term.GetFdInfo(out) + return &Out{ + out: out, + CommonStream: CommonStream{ + Fd: fd, + IsTerminal: isTerminal, + }, + } +} diff --git a/pkg/commands/streamer/streamer.go b/pkg/commands/streamer/streamer.go new file mode 100644 index 00000000..ed191cb2 --- /dev/null +++ b/pkg/commands/streamer/streamer.go @@ -0,0 +1,185 @@ +// adapted from Skanehira's docui package which is no longer maintained + +package streamer + +import ( + "context" + "errors" + "io" + "log" + "os" + "sync" + "time" + + "github.com/docker/docker/api/types" + "github.com/moby/term" + "github.com/sirupsen/logrus" +) + +type ResizeContainer func(ctx context.Context, id string, options types.ResizeOptions) error + +var ( + ErrEmptyExecID = errors.New("empty exec id") + ErrTtySizeIsZero = errors.New("tty size is 0") +) + +type Streamer struct { + In *In + Out *Out + Err io.Writer + isTty bool + Log *logrus.Entry +} + +func New(logger *logrus.Entry) *Streamer { + return &Streamer{ + In: NewIn(os.Stdin), + Out: NewOut(os.Stdout), + Err: os.Stderr, + Log: logger, + } +} + +func (s *Streamer) Stream(ctx context.Context, id string, resp types.HijackedResponse, resize ResizeContainer) (err error) { + if id == "" { + return ErrEmptyExecID + } + + errCh := make(chan error, 1) + + go func() { + defer close(errCh) + errCh <- s.stream(ctx, resp) + }() + + if s.In.IsTerminal { + s.monitorTtySize(ctx, resize, id) + } + + if err := <-errCh; err != nil { + s.Log.Errorf("stream error: %s", err) + return err + } + + return nil +} + +func (s *Streamer) stream(ctx context.Context, resp types.HijackedResponse) error { + // set raw mode + restore, err := s.SetRawTerminal() + if err != nil { + return err + } + defer restore() + + // start stdin/stdout stream + outDone := s.streamOut(restore, resp) + inDone := s.streamIn(restore, resp) + + select { + case err := <-outDone: + return err + case <-inDone: + select { + case err := <-outDone: + return err + case <-ctx.Done(): + return ctx.Err() + } + case <-ctx.Done(): + return ctx.Err() + } +} + +func (s *Streamer) streamIn(restore func(), resp types.HijackedResponse) <-chan struct{} { + done := make(chan struct{}) + + go func() { + s.Log.Warn("in streamIn") + defer close(done) + defer restore() + _, err := io.Copy(resp.Conn, s.In) + + if _, ok := err.(term.EscapeError); ok { + s.Log.Warn("not returning from here") + return + } + + if err != nil { + s.Log.Errorf("in stream error: %s", err) + return + } + + if err := resp.CloseWrite(); err != nil { + s.Log.Errorf("close response error: %s", err) + } + }() + + return done +} + +func (s *Streamer) streamOut(restore func(), resp types.HijackedResponse) <-chan error { + done := make(chan error, 1) + + go func() { + _, err := io.Copy(s.Out, resp.Reader) + restore() + + if err != nil { + s.Log.Errorf("output stream error: %s", err) + return + } + + done <- err + }() + + return done +} + +func (s *Streamer) SetRawTerminal() (func(), error) { + if err := s.In.SetRawTerminal(); err != nil { + return nil, err + } + + var once sync.Once + restore := func() { + once.Do(func() { + if err := s.In.RestoreTerminal(); err != nil { + s.Log.Errorf("failed to restore terminal: %s\n", err) + } + }) + } + + return restore, nil +} + +func (s *Streamer) resizeTty(ctx context.Context, resize ResizeContainer, id string) error { + h, w := s.Out.GetTtySize() + if h == 0 && w == 0 { + return ErrTtySizeIsZero + } + + options := types.ResizeOptions{ + Height: h, + Width: w, + } + + return resize(ctx, id, options) +} + +func (s *Streamer) initTtySize(ctx context.Context, resize ResizeContainer, id string) { + if err := s.resizeTty(ctx, resize, id); err != nil { + go func() { + s.Log.Errorf("failed to resize tty: %s\n", err) + for retry := 0; retry < 5; retry++ { + time.Sleep(10 * time.Millisecond) + if err = s.resizeTty(ctx, resize, id); err == nil { + break + } + } + if err != nil { + log.Println("failed to resize tty, using default size") + } + }() + } +} diff --git a/pkg/commands/streamer/streamer_unix.go b/pkg/commands/streamer/streamer_unix.go new file mode 100644 index 00000000..0fb16f04 --- /dev/null +++ b/pkg/commands/streamer/streamer_unix.go @@ -0,0 +1,22 @@ +//go:build !windows +// +build !windows + +package streamer + +import ( + "context" + "os" + "os/signal" + "syscall" +) + +func (s *Streamer) monitorTtySize(ctx context.Context, resize ResizeContainer, id string) { + s.initTtySize(ctx, resize, id) + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, syscall.SIGWINCH) + go func() { + for range sigchan { + _ = s.resizeTty(ctx, resize, id) + } + }() +} diff --git a/pkg/commands/streamer/streamer_windows.go b/pkg/commands/streamer/streamer_windows.go new file mode 100644 index 00000000..8c7e6ff2 --- /dev/null +++ b/pkg/commands/streamer/streamer_windows.go @@ -0,0 +1,21 @@ +//go:build windows +// +build windows + +package streamer + +import ( + "context" +) + +func (s *Streamer) monitorTtySize(ctx context.Context, resize ResizeContainer, id string) { + /* TODO: mattn: Currently, this is not supported on Windows. + s.initTtySize(ctx, resize, id) + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, syscall.SIGWINCH) + go func() { + for range sigchan { + s.resizeTty(ctx, resize, id) + } + }() + */ +} diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index c50e1985..8d0cb95c 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -523,16 +523,28 @@ func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error { if err != nil { return nil } - commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{ - Container: container, - }) - // TODO: use SDK - resolvedCommand := utils.ApplyTemplate("docker exec -it {{ .Container.ID }} /bin/sh -c 'eval $(grep ^$(id -un): /etc/passwd | cut -d : -f 7-)'", commandObject) - // attach and return the subprocess error - cmd := gui.OSCommand.ExecutableFromString(resolvedCommand) - gui.SubProcess = cmd - return gui.Errors.ErrSubProcess + if err := gui.g.Suspend(); err != nil { + gui.Log.Error(err) + return nil + } + + defer func() { + if err := gui.g.Resume(); err != nil { + gui.Log.Error(err) + } + }() + + err = gui.DockerCommand.AttachExecContainer( + container.ID, + []string{"/bin/sh", "-c", "eval $(grep ^$(id -un): /etc/passwd | cut -d : -f 7-)"}, + ) + + if err != nil { + gui.ErrorChan <- err + } + + return nil } func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error { diff --git a/vendor/github.com/Azure/go-ansiterm/LICENSE b/vendor/github.com/Azure/go-ansiterm/LICENSE new file mode 100644 index 00000000..e3d9a64d --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/Azure/go-ansiterm/README.md b/vendor/github.com/Azure/go-ansiterm/README.md new file mode 100644 index 00000000..261c041e --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/README.md @@ -0,0 +1,12 @@ +# go-ansiterm + +This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent. + +For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position. + +The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go). + +See parser_test.go for examples exercising the state machine and generating appropriate function calls. + +----- +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/vendor/github.com/Azure/go-ansiterm/constants.go b/vendor/github.com/Azure/go-ansiterm/constants.go new file mode 100644 index 00000000..96504a33 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/constants.go @@ -0,0 +1,188 @@ +package ansiterm + +const LogEnv = "DEBUG_TERMINAL" + +// ANSI constants +// References: +// -- http://www.ecma-international.org/publications/standards/Ecma-048.htm +// -- http://man7.org/linux/man-pages/man4/console_codes.4.html +// -- http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html +// -- http://en.wikipedia.org/wiki/ANSI_escape_code +// -- http://vt100.net/emu/dec_ansi_parser +// -- http://vt100.net/emu/vt500_parser.svg +// -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html +// -- http://www.inwap.com/pdp10/ansicode.txt +const ( + // ECMA-48 Set Graphics Rendition + // Note: + // -- Constants leading with an underscore (e.g., _ANSI_xxx) are unsupported or reserved + // -- Fonts could possibly be supported via SetCurrentConsoleFontEx + // -- Windows does not expose the per-window cursor (i.e., caret) blink times + ANSI_SGR_RESET = 0 + ANSI_SGR_BOLD = 1 + ANSI_SGR_DIM = 2 + _ANSI_SGR_ITALIC = 3 + ANSI_SGR_UNDERLINE = 4 + _ANSI_SGR_BLINKSLOW = 5 + _ANSI_SGR_BLINKFAST = 6 + ANSI_SGR_REVERSE = 7 + _ANSI_SGR_INVISIBLE = 8 + _ANSI_SGR_LINETHROUGH = 9 + _ANSI_SGR_FONT_00 = 10 + _ANSI_SGR_FONT_01 = 11 + _ANSI_SGR_FONT_02 = 12 + _ANSI_SGR_FONT_03 = 13 + _ANSI_SGR_FONT_04 = 14 + _ANSI_SGR_FONT_05 = 15 + _ANSI_SGR_FONT_06 = 16 + _ANSI_SGR_FONT_07 = 17 + _ANSI_SGR_FONT_08 = 18 + _ANSI_SGR_FONT_09 = 19 + _ANSI_SGR_FONT_10 = 20 + _ANSI_SGR_DOUBLEUNDERLINE = 21 + ANSI_SGR_BOLD_DIM_OFF = 22 + _ANSI_SGR_ITALIC_OFF = 23 + ANSI_SGR_UNDERLINE_OFF = 24 + _ANSI_SGR_BLINK_OFF = 25 + _ANSI_SGR_RESERVED_00 = 26 + ANSI_SGR_REVERSE_OFF = 27 + _ANSI_SGR_INVISIBLE_OFF = 28 + _ANSI_SGR_LINETHROUGH_OFF = 29 + ANSI_SGR_FOREGROUND_BLACK = 30 + ANSI_SGR_FOREGROUND_RED = 31 + ANSI_SGR_FOREGROUND_GREEN = 32 + ANSI_SGR_FOREGROUND_YELLOW = 33 + ANSI_SGR_FOREGROUND_BLUE = 34 + ANSI_SGR_FOREGROUND_MAGENTA = 35 + ANSI_SGR_FOREGROUND_CYAN = 36 + ANSI_SGR_FOREGROUND_WHITE = 37 + _ANSI_SGR_RESERVED_01 = 38 + ANSI_SGR_FOREGROUND_DEFAULT = 39 + ANSI_SGR_BACKGROUND_BLACK = 40 + ANSI_SGR_BACKGROUND_RED = 41 + ANSI_SGR_BACKGROUND_GREEN = 42 + ANSI_SGR_BACKGROUND_YELLOW = 43 + ANSI_SGR_BACKGROUND_BLUE = 44 + ANSI_SGR_BACKGROUND_MAGENTA = 45 + ANSI_SGR_BACKGROUND_CYAN = 46 + ANSI_SGR_BACKGROUND_WHITE = 47 + _ANSI_SGR_RESERVED_02 = 48 + ANSI_SGR_BACKGROUND_DEFAULT = 49 + // 50 - 65: Unsupported + + ANSI_MAX_CMD_LENGTH = 4096 + + MAX_INPUT_EVENTS = 128 + DEFAULT_WIDTH = 80 + DEFAULT_HEIGHT = 24 + + ANSI_BEL = 0x07 + ANSI_BACKSPACE = 0x08 + ANSI_TAB = 0x09 + ANSI_LINE_FEED = 0x0A + ANSI_VERTICAL_TAB = 0x0B + ANSI_FORM_FEED = 0x0C + ANSI_CARRIAGE_RETURN = 0x0D + ANSI_ESCAPE_PRIMARY = 0x1B + ANSI_ESCAPE_SECONDARY = 0x5B + ANSI_OSC_STRING_ENTRY = 0x5D + ANSI_COMMAND_FIRST = 0x40 + ANSI_COMMAND_LAST = 0x7E + DCS_ENTRY = 0x90 + CSI_ENTRY = 0x9B + OSC_STRING = 0x9D + ANSI_PARAMETER_SEP = ";" + ANSI_CMD_G0 = '(' + ANSI_CMD_G1 = ')' + ANSI_CMD_G2 = '*' + ANSI_CMD_G3 = '+' + ANSI_CMD_DECPNM = '>' + ANSI_CMD_DECPAM = '=' + ANSI_CMD_OSC = ']' + ANSI_CMD_STR_TERM = '\\' + + KEY_CONTROL_PARAM_2 = ";2" + KEY_CONTROL_PARAM_3 = ";3" + KEY_CONTROL_PARAM_4 = ";4" + KEY_CONTROL_PARAM_5 = ";5" + KEY_CONTROL_PARAM_6 = ";6" + KEY_CONTROL_PARAM_7 = ";7" + KEY_CONTROL_PARAM_8 = ";8" + KEY_ESC_CSI = "\x1B[" + KEY_ESC_N = "\x1BN" + KEY_ESC_O = "\x1BO" + + FILL_CHARACTER = ' ' +) + +func getByteRange(start byte, end byte) []byte { + bytes := make([]byte, 0, 32) + for i := start; i <= end; i++ { + bytes = append(bytes, byte(i)) + } + + return bytes +} + +var toGroundBytes = getToGroundBytes() +var executors = getExecuteBytes() + +// SPACE 20+A0 hex Always and everywhere a blank space +// Intermediate 20-2F hex !"#$%&'()*+,-./ +var intermeds = getByteRange(0x20, 0x2F) + +// Parameters 30-3F hex 0123456789:;<=>? +// CSI Parameters 30-39, 3B hex 0123456789; +var csiParams = getByteRange(0x30, 0x3F) + +var csiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...) + +// Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +var upperCase = getByteRange(0x40, 0x5F) + +// Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~ +var lowerCase = getByteRange(0x60, 0x7E) + +// Alphabetics 40-7E hex (all of upper and lower case) +var alphabetics = append(upperCase, lowerCase...) + +var printables = getByteRange(0x20, 0x7F) + +var escapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E) +var escapeToGroundBytes = getEscapeToGroundBytes() + +// See http://www.vt100.net/emu/vt500_parser.png for description of the complex +// byte ranges below + +func getEscapeToGroundBytes() []byte { + escapeToGroundBytes := getByteRange(0x30, 0x4F) + escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x51, 0x57)...) + escapeToGroundBytes = append(escapeToGroundBytes, 0x59) + escapeToGroundBytes = append(escapeToGroundBytes, 0x5A) + escapeToGroundBytes = append(escapeToGroundBytes, 0x5C) + escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x60, 0x7E)...) + return escapeToGroundBytes +} + +func getExecuteBytes() []byte { + executeBytes := getByteRange(0x00, 0x17) + executeBytes = append(executeBytes, 0x19) + executeBytes = append(executeBytes, getByteRange(0x1C, 0x1F)...) + return executeBytes +} + +func getToGroundBytes() []byte { + groundBytes := []byte{0x18} + groundBytes = append(groundBytes, 0x1A) + groundBytes = append(groundBytes, getByteRange(0x80, 0x8F)...) + groundBytes = append(groundBytes, getByteRange(0x91, 0x97)...) + groundBytes = append(groundBytes, 0x99) + groundBytes = append(groundBytes, 0x9A) + groundBytes = append(groundBytes, 0x9C) + return groundBytes +} + +// Delete 7F hex Always and everywhere ignored +// C1 Control 80-9F hex 32 additional control characters +// G1 Displayable A1-FE hex 94 additional displayable characters +// Special A0+FF hex Same as SPACE and DELETE diff --git a/vendor/github.com/Azure/go-ansiterm/context.go b/vendor/github.com/Azure/go-ansiterm/context.go new file mode 100644 index 00000000..8d66e777 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/context.go @@ -0,0 +1,7 @@ +package ansiterm + +type ansiContext struct { + currentChar byte + paramBuffer []byte + interBuffer []byte +} diff --git a/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go b/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go new file mode 100644 index 00000000..bcbe00d0 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go @@ -0,0 +1,49 @@ +package ansiterm + +type csiEntryState struct { + baseState +} + +func (csiState csiEntryState) Handle(b byte) (s state, e error) { + csiState.parser.logf("CsiEntry::Handle %#x", b) + + nextState, err := csiState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case sliceContains(alphabetics, b): + return csiState.parser.ground, nil + case sliceContains(csiCollectables, b): + return csiState.parser.csiParam, nil + case sliceContains(executors, b): + return csiState, csiState.parser.execute() + } + + return csiState, nil +} + +func (csiState csiEntryState) Transition(s state) error { + csiState.parser.logf("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name()) + csiState.baseState.Transition(s) + + switch s { + case csiState.parser.ground: + return csiState.parser.csiDispatch() + case csiState.parser.csiParam: + switch { + case sliceContains(csiParams, csiState.parser.context.currentChar): + csiState.parser.collectParam() + case sliceContains(intermeds, csiState.parser.context.currentChar): + csiState.parser.collectInter() + } + } + + return nil +} + +func (csiState csiEntryState) Enter() error { + csiState.parser.clear() + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/csi_param_state.go b/vendor/github.com/Azure/go-ansiterm/csi_param_state.go new file mode 100644 index 00000000..7ed5e01c --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/csi_param_state.go @@ -0,0 +1,38 @@ +package ansiterm + +type csiParamState struct { + baseState +} + +func (csiState csiParamState) Handle(b byte) (s state, e error) { + csiState.parser.logf("CsiParam::Handle %#x", b) + + nextState, err := csiState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case sliceContains(alphabetics, b): + return csiState.parser.ground, nil + case sliceContains(csiCollectables, b): + csiState.parser.collectParam() + return csiState, nil + case sliceContains(executors, b): + return csiState, csiState.parser.execute() + } + + return csiState, nil +} + +func (csiState csiParamState) Transition(s state) error { + csiState.parser.logf("CsiParam::Transition %s --> %s", csiState.Name(), s.Name()) + csiState.baseState.Transition(s) + + switch s { + case csiState.parser.ground: + return csiState.parser.csiDispatch() + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go b/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go new file mode 100644 index 00000000..1c719db9 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go @@ -0,0 +1,36 @@ +package ansiterm + +type escapeIntermediateState struct { + baseState +} + +func (escState escapeIntermediateState) Handle(b byte) (s state, e error) { + escState.parser.logf("escapeIntermediateState::Handle %#x", b) + nextState, err := escState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case sliceContains(intermeds, b): + return escState, escState.parser.collectInter() + case sliceContains(executors, b): + return escState, escState.parser.execute() + case sliceContains(escapeIntermediateToGroundBytes, b): + return escState.parser.ground, nil + } + + return escState, nil +} + +func (escState escapeIntermediateState) Transition(s state) error { + escState.parser.logf("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name()) + escState.baseState.Transition(s) + + switch s { + case escState.parser.ground: + return escState.parser.escDispatch() + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/escape_state.go b/vendor/github.com/Azure/go-ansiterm/escape_state.go new file mode 100644 index 00000000..6390abd2 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/escape_state.go @@ -0,0 +1,47 @@ +package ansiterm + +type escapeState struct { + baseState +} + +func (escState escapeState) Handle(b byte) (s state, e error) { + escState.parser.logf("escapeState::Handle %#x", b) + nextState, err := escState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case b == ANSI_ESCAPE_SECONDARY: + return escState.parser.csiEntry, nil + case b == ANSI_OSC_STRING_ENTRY: + return escState.parser.oscString, nil + case sliceContains(executors, b): + return escState, escState.parser.execute() + case sliceContains(escapeToGroundBytes, b): + return escState.parser.ground, nil + case sliceContains(intermeds, b): + return escState.parser.escapeIntermediate, nil + } + + return escState, nil +} + +func (escState escapeState) Transition(s state) error { + escState.parser.logf("Escape::Transition %s --> %s", escState.Name(), s.Name()) + escState.baseState.Transition(s) + + switch s { + case escState.parser.ground: + return escState.parser.escDispatch() + case escState.parser.escapeIntermediate: + return escState.parser.collectInter() + } + + return nil +} + +func (escState escapeState) Enter() error { + escState.parser.clear() + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/event_handler.go b/vendor/github.com/Azure/go-ansiterm/event_handler.go new file mode 100644 index 00000000..98087b38 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/event_handler.go @@ -0,0 +1,90 @@ +package ansiterm + +type AnsiEventHandler interface { + // Print + Print(b byte) error + + // Execute C0 commands + Execute(b byte) error + + // CUrsor Up + CUU(int) error + + // CUrsor Down + CUD(int) error + + // CUrsor Forward + CUF(int) error + + // CUrsor Backward + CUB(int) error + + // Cursor to Next Line + CNL(int) error + + // Cursor to Previous Line + CPL(int) error + + // Cursor Horizontal position Absolute + CHA(int) error + + // Vertical line Position Absolute + VPA(int) error + + // CUrsor Position + CUP(int, int) error + + // Horizontal and Vertical Position (depends on PUM) + HVP(int, int) error + + // Text Cursor Enable Mode + DECTCEM(bool) error + + // Origin Mode + DECOM(bool) error + + // 132 Column Mode + DECCOLM(bool) error + + // Erase in Display + ED(int) error + + // Erase in Line + EL(int) error + + // Insert Line + IL(int) error + + // Delete Line + DL(int) error + + // Insert Character + ICH(int) error + + // Delete Character + DCH(int) error + + // Set Graphics Rendition + SGR([]int) error + + // Pan Down + SU(int) error + + // Pan Up + SD(int) error + + // Device Attributes + DA([]string) error + + // Set Top and Bottom Margins + DECSTBM(int, int) error + + // Index + IND() error + + // Reverse Index + RI() error + + // Flush updates from previous commands + Flush() error +} diff --git a/vendor/github.com/Azure/go-ansiterm/ground_state.go b/vendor/github.com/Azure/go-ansiterm/ground_state.go new file mode 100644 index 00000000..52451e94 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/ground_state.go @@ -0,0 +1,24 @@ +package ansiterm + +type groundState struct { + baseState +} + +func (gs groundState) Handle(b byte) (s state, e error) { + gs.parser.context.currentChar = b + + nextState, err := gs.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case sliceContains(printables, b): + return gs, gs.parser.print() + + case sliceContains(executors, b): + return gs, gs.parser.execute() + } + + return gs, nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/osc_string_state.go b/vendor/github.com/Azure/go-ansiterm/osc_string_state.go new file mode 100644 index 00000000..593b10ab --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/osc_string_state.go @@ -0,0 +1,31 @@ +package ansiterm + +type oscStringState struct { + baseState +} + +func (oscState oscStringState) Handle(b byte) (s state, e error) { + oscState.parser.logf("OscString::Handle %#x", b) + nextState, err := oscState.baseState.Handle(b) + if nextState != nil || err != nil { + return nextState, err + } + + switch { + case isOscStringTerminator(b): + return oscState.parser.ground, nil + } + + return oscState, nil +} + +// See below for OSC string terminators for linux +// http://man7.org/linux/man-pages/man4/console_codes.4.html +func isOscStringTerminator(b byte) bool { + + if b == ANSI_BEL || b == 0x5C { + return true + } + + return false +} diff --git a/vendor/github.com/Azure/go-ansiterm/parser.go b/vendor/github.com/Azure/go-ansiterm/parser.go new file mode 100644 index 00000000..03cec7ad --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/parser.go @@ -0,0 +1,151 @@ +package ansiterm + +import ( + "errors" + "log" + "os" +) + +type AnsiParser struct { + currState state + eventHandler AnsiEventHandler + context *ansiContext + csiEntry state + csiParam state + dcsEntry state + escape state + escapeIntermediate state + error state + ground state + oscString state + stateMap []state + + logf func(string, ...interface{}) +} + +type Option func(*AnsiParser) + +func WithLogf(f func(string, ...interface{})) Option { + return func(ap *AnsiParser) { + ap.logf = f + } +} + +func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser { + ap := &AnsiParser{ + eventHandler: evtHandler, + context: &ansiContext{}, + } + for _, o := range opts { + o(ap) + } + + if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" { + logFile, _ := os.Create("ansiParser.log") + logger := log.New(logFile, "", log.LstdFlags) + if ap.logf != nil { + l := ap.logf + ap.logf = func(s string, v ...interface{}) { + l(s, v...) + logger.Printf(s, v...) + } + } else { + ap.logf = logger.Printf + } + } + + if ap.logf == nil { + ap.logf = func(string, ...interface{}) {} + } + + ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}} + ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}} + ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}} + ap.escape = escapeState{baseState{name: "Escape", parser: ap}} + ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}} + ap.error = errorState{baseState{name: "Error", parser: ap}} + ap.ground = groundState{baseState{name: "Ground", parser: ap}} + ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}} + + ap.stateMap = []state{ + ap.csiEntry, + ap.csiParam, + ap.dcsEntry, + ap.escape, + ap.escapeIntermediate, + ap.error, + ap.ground, + ap.oscString, + } + + ap.currState = getState(initialState, ap.stateMap) + + ap.logf("CreateParser: parser %p", ap) + return ap +} + +func getState(name string, states []state) state { + for _, el := range states { + if el.Name() == name { + return el + } + } + + return nil +} + +func (ap *AnsiParser) Parse(bytes []byte) (int, error) { + for i, b := range bytes { + if err := ap.handle(b); err != nil { + return i, err + } + } + + return len(bytes), ap.eventHandler.Flush() +} + +func (ap *AnsiParser) handle(b byte) error { + ap.context.currentChar = b + newState, err := ap.currState.Handle(b) + if err != nil { + return err + } + + if newState == nil { + ap.logf("WARNING: newState is nil") + return errors.New("New state of 'nil' is invalid.") + } + + if newState != ap.currState { + if err := ap.changeState(newState); err != nil { + return err + } + } + + return nil +} + +func (ap *AnsiParser) changeState(newState state) error { + ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name()) + + // Exit old state + if err := ap.currState.Exit(); err != nil { + ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err) + return err + } + + // Perform transition action + if err := ap.currState.Transition(newState); err != nil { + ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err) + return err + } + + // Enter new state + if err := newState.Enter(); err != nil { + ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err) + return err + } + + ap.currState = newState + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go b/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go new file mode 100644 index 00000000..de0a1f9c --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go @@ -0,0 +1,99 @@ +package ansiterm + +import ( + "strconv" +) + +func parseParams(bytes []byte) ([]string, error) { + paramBuff := make([]byte, 0, 0) + params := []string{} + + for _, v := range bytes { + if v == ';' { + if len(paramBuff) > 0 { + // Completed parameter, append it to the list + s := string(paramBuff) + params = append(params, s) + paramBuff = make([]byte, 0, 0) + } + } else { + paramBuff = append(paramBuff, v) + } + } + + // Last parameter may not be terminated with ';' + if len(paramBuff) > 0 { + s := string(paramBuff) + params = append(params, s) + } + + return params, nil +} + +func parseCmd(context ansiContext) (string, error) { + return string(context.currentChar), nil +} + +func getInt(params []string, dflt int) int { + i := getInts(params, 1, dflt)[0] + return i +} + +func getInts(params []string, minCount int, dflt int) []int { + ints := []int{} + + for _, v := range params { + i, _ := strconv.Atoi(v) + // Zero is mapped to the default value in VT100. + if i == 0 { + i = dflt + } + ints = append(ints, i) + } + + if len(ints) < minCount { + remaining := minCount - len(ints) + for i := 0; i < remaining; i++ { + ints = append(ints, dflt) + } + } + + return ints +} + +func (ap *AnsiParser) modeDispatch(param string, set bool) error { + switch param { + case "?3": + return ap.eventHandler.DECCOLM(set) + case "?6": + return ap.eventHandler.DECOM(set) + case "?25": + return ap.eventHandler.DECTCEM(set) + } + return nil +} + +func (ap *AnsiParser) hDispatch(params []string) error { + if len(params) == 1 { + return ap.modeDispatch(params[0], true) + } + + return nil +} + +func (ap *AnsiParser) lDispatch(params []string) error { + if len(params) == 1 { + return ap.modeDispatch(params[0], false) + } + + return nil +} + +func getEraseParam(params []string) int { + param := getInt(params, 0) + if param < 0 || 3 < param { + param = 0 + } + + return param +} diff --git a/vendor/github.com/Azure/go-ansiterm/parser_actions.go b/vendor/github.com/Azure/go-ansiterm/parser_actions.go new file mode 100644 index 00000000..0bb5e51e --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/parser_actions.go @@ -0,0 +1,119 @@ +package ansiterm + +func (ap *AnsiParser) collectParam() error { + currChar := ap.context.currentChar + ap.logf("collectParam %#x", currChar) + ap.context.paramBuffer = append(ap.context.paramBuffer, currChar) + return nil +} + +func (ap *AnsiParser) collectInter() error { + currChar := ap.context.currentChar + ap.logf("collectInter %#x", currChar) + ap.context.paramBuffer = append(ap.context.interBuffer, currChar) + return nil +} + +func (ap *AnsiParser) escDispatch() error { + cmd, _ := parseCmd(*ap.context) + intermeds := ap.context.interBuffer + ap.logf("escDispatch currentChar: %#x", ap.context.currentChar) + ap.logf("escDispatch: %v(%v)", cmd, intermeds) + + switch cmd { + case "D": // IND + return ap.eventHandler.IND() + case "E": // NEL, equivalent to CRLF + err := ap.eventHandler.Execute(ANSI_CARRIAGE_RETURN) + if err == nil { + err = ap.eventHandler.Execute(ANSI_LINE_FEED) + } + return err + case "M": // RI + return ap.eventHandler.RI() + } + + return nil +} + +func (ap *AnsiParser) csiDispatch() error { + cmd, _ := parseCmd(*ap.context) + params, _ := parseParams(ap.context.paramBuffer) + ap.logf("Parsed params: %v with length: %d", params, len(params)) + + ap.logf("csiDispatch: %v(%v)", cmd, params) + + switch cmd { + case "@": + return ap.eventHandler.ICH(getInt(params, 1)) + case "A": + return ap.eventHandler.CUU(getInt(params, 1)) + case "B": + return ap.eventHandler.CUD(getInt(params, 1)) + case "C": + return ap.eventHandler.CUF(getInt(params, 1)) + case "D": + return ap.eventHandler.CUB(getInt(params, 1)) + case "E": + return ap.eventHandler.CNL(getInt(params, 1)) + case "F": + return ap.eventHandler.CPL(getInt(params, 1)) + case "G": + return ap.eventHandler.CHA(getInt(params, 1)) + case "H": + ints := getInts(params, 2, 1) + x, y := ints[0], ints[1] + return ap.eventHandler.CUP(x, y) + case "J": + param := getEraseParam(params) + return ap.eventHandler.ED(param) + case "K": + param := getEraseParam(params) + return ap.eventHandler.EL(param) + case "L": + return ap.eventHandler.IL(getInt(params, 1)) + case "M": + return ap.eventHandler.DL(getInt(params, 1)) + case "P": + return ap.eventHandler.DCH(getInt(params, 1)) + case "S": + return ap.eventHandler.SU(getInt(params, 1)) + case "T": + return ap.eventHandler.SD(getInt(params, 1)) + case "c": + return ap.eventHandler.DA(params) + case "d": + return ap.eventHandler.VPA(getInt(params, 1)) + case "f": + ints := getInts(params, 2, 1) + x, y := ints[0], ints[1] + return ap.eventHandler.HVP(x, y) + case "h": + return ap.hDispatch(params) + case "l": + return ap.lDispatch(params) + case "m": + return ap.eventHandler.SGR(getInts(params, 1, 0)) + case "r": + ints := getInts(params, 2, 1) + top, bottom := ints[0], ints[1] + return ap.eventHandler.DECSTBM(top, bottom) + default: + ap.logf("ERROR: Unsupported CSI command: '%s', with full context: %v", cmd, ap.context) + return nil + } + +} + +func (ap *AnsiParser) print() error { + return ap.eventHandler.Print(ap.context.currentChar) +} + +func (ap *AnsiParser) clear() error { + ap.context = &ansiContext{} + return nil +} + +func (ap *AnsiParser) execute() error { + return ap.eventHandler.Execute(ap.context.currentChar) +} diff --git a/vendor/github.com/Azure/go-ansiterm/states.go b/vendor/github.com/Azure/go-ansiterm/states.go new file mode 100644 index 00000000..f2ea1fcd --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/states.go @@ -0,0 +1,71 @@ +package ansiterm + +type stateID int + +type state interface { + Enter() error + Exit() error + Handle(byte) (state, error) + Name() string + Transition(state) error +} + +type baseState struct { + name string + parser *AnsiParser +} + +func (base baseState) Enter() error { + return nil +} + +func (base baseState) Exit() error { + return nil +} + +func (base baseState) Handle(b byte) (s state, e error) { + + switch { + case b == CSI_ENTRY: + return base.parser.csiEntry, nil + case b == DCS_ENTRY: + return base.parser.dcsEntry, nil + case b == ANSI_ESCAPE_PRIMARY: + return base.parser.escape, nil + case b == OSC_STRING: + return base.parser.oscString, nil + case sliceContains(toGroundBytes, b): + return base.parser.ground, nil + } + + return nil, nil +} + +func (base baseState) Name() string { + return base.name +} + +func (base baseState) Transition(s state) error { + if s == base.parser.ground { + execBytes := []byte{0x18} + execBytes = append(execBytes, 0x1A) + execBytes = append(execBytes, getByteRange(0x80, 0x8F)...) + execBytes = append(execBytes, getByteRange(0x91, 0x97)...) + execBytes = append(execBytes, 0x99) + execBytes = append(execBytes, 0x9A) + + if sliceContains(execBytes, base.parser.context.currentChar) { + return base.parser.execute() + } + } + + return nil +} + +type dcsEntryState struct { + baseState +} + +type errorState struct { + baseState +} diff --git a/vendor/github.com/Azure/go-ansiterm/utilities.go b/vendor/github.com/Azure/go-ansiterm/utilities.go new file mode 100644 index 00000000..39211449 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/utilities.go @@ -0,0 +1,21 @@ +package ansiterm + +import ( + "strconv" +) + +func sliceContains(bytes []byte, b byte) bool { + for _, v := range bytes { + if v == b { + return true + } + } + + return false +} + +func convertBytesToInteger(bytes []byte) int { + s := string(bytes) + i, _ := strconv.Atoi(s) + return i +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go b/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go new file mode 100644 index 00000000..5599082a --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go @@ -0,0 +1,196 @@ +// +build windows + +package winterm + +import ( + "fmt" + "os" + "strconv" + "strings" + "syscall" + + "github.com/Azure/go-ansiterm" + windows "golang.org/x/sys/windows" +) + +// Windows keyboard constants +// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx. +const ( + VK_PRIOR = 0x21 // PAGE UP key + VK_NEXT = 0x22 // PAGE DOWN key + VK_END = 0x23 // END key + VK_HOME = 0x24 // HOME key + VK_LEFT = 0x25 // LEFT ARROW key + VK_UP = 0x26 // UP ARROW key + VK_RIGHT = 0x27 // RIGHT ARROW key + VK_DOWN = 0x28 // DOWN ARROW key + VK_SELECT = 0x29 // SELECT key + VK_PRINT = 0x2A // PRINT key + VK_EXECUTE = 0x2B // EXECUTE key + VK_SNAPSHOT = 0x2C // PRINT SCREEN key + VK_INSERT = 0x2D // INS key + VK_DELETE = 0x2E // DEL key + VK_HELP = 0x2F // HELP key + VK_F1 = 0x70 // F1 key + VK_F2 = 0x71 // F2 key + VK_F3 = 0x72 // F3 key + VK_F4 = 0x73 // F4 key + VK_F5 = 0x74 // F5 key + VK_F6 = 0x75 // F6 key + VK_F7 = 0x76 // F7 key + VK_F8 = 0x77 // F8 key + VK_F9 = 0x78 // F9 key + VK_F10 = 0x79 // F10 key + VK_F11 = 0x7A // F11 key + VK_F12 = 0x7B // F12 key + + RIGHT_ALT_PRESSED = 0x0001 + LEFT_ALT_PRESSED = 0x0002 + RIGHT_CTRL_PRESSED = 0x0004 + LEFT_CTRL_PRESSED = 0x0008 + SHIFT_PRESSED = 0x0010 + NUMLOCK_ON = 0x0020 + SCROLLLOCK_ON = 0x0040 + CAPSLOCK_ON = 0x0080 + ENHANCED_KEY = 0x0100 +) + +type ansiCommand struct { + CommandBytes []byte + Command string + Parameters []string + IsSpecial bool +} + +func newAnsiCommand(command []byte) *ansiCommand { + + if isCharacterSelectionCmdChar(command[1]) { + // Is Character Set Selection commands + return &ansiCommand{ + CommandBytes: command, + Command: string(command), + IsSpecial: true, + } + } + + // last char is command character + lastCharIndex := len(command) - 1 + + ac := &ansiCommand{ + CommandBytes: command, + Command: string(command[lastCharIndex]), + IsSpecial: false, + } + + // more than a single escape + if lastCharIndex != 0 { + start := 1 + // skip if double char escape sequence + if command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_ESCAPE_SECONDARY { + start++ + } + // convert this to GetNextParam method + ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ansiterm.ANSI_PARAMETER_SEP) + } + + return ac +} + +func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 { + if index < 0 || index >= len(ac.Parameters) { + return defaultValue + } + + param, err := strconv.ParseInt(ac.Parameters[index], 10, 16) + if err != nil { + return defaultValue + } + + return int16(param) +} + +func (ac *ansiCommand) String() string { + return fmt.Sprintf("0x%v \"%v\" (\"%v\")", + bytesToHex(ac.CommandBytes), + ac.Command, + strings.Join(ac.Parameters, "\",\"")) +} + +// isAnsiCommandChar returns true if the passed byte falls within the range of ANSI commands. +// See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html. +func isAnsiCommandChar(b byte) bool { + switch { + case ansiterm.ANSI_COMMAND_FIRST <= b && b <= ansiterm.ANSI_COMMAND_LAST && b != ansiterm.ANSI_ESCAPE_SECONDARY: + return true + case b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_OSC || b == ansiterm.ANSI_CMD_DECPAM || b == ansiterm.ANSI_CMD_DECPNM: + // non-CSI escape sequence terminator + return true + case b == ansiterm.ANSI_CMD_STR_TERM || b == ansiterm.ANSI_BEL: + // String escape sequence terminator + return true + } + return false +} + +func isXtermOscSequence(command []byte, current byte) bool { + return (len(command) >= 2 && command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_CMD_OSC && current != ansiterm.ANSI_BEL) +} + +func isCharacterSelectionCmdChar(b byte) bool { + return (b == ansiterm.ANSI_CMD_G0 || b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_G2 || b == ansiterm.ANSI_CMD_G3) +} + +// bytesToHex converts a slice of bytes to a human-readable string. +func bytesToHex(b []byte) string { + hex := make([]string, len(b)) + for i, ch := range b { + hex[i] = fmt.Sprintf("%X", ch) + } + return strings.Join(hex, "") +} + +// ensureInRange adjusts the passed value, if necessary, to ensure it is within +// the passed min / max range. +func ensureInRange(n int16, min int16, max int16) int16 { + if n < min { + return min + } else if n > max { + return max + } else { + return n + } +} + +func GetStdFile(nFile int) (*os.File, uintptr) { + var file *os.File + + // syscall uses negative numbers + // windows package uses very big uint32 + // Keep these switches split so we don't have to convert ints too much. + switch uint32(nFile) { + case windows.STD_INPUT_HANDLE: + file = os.Stdin + case windows.STD_OUTPUT_HANDLE: + file = os.Stdout + case windows.STD_ERROR_HANDLE: + file = os.Stderr + default: + switch nFile { + case syscall.STD_INPUT_HANDLE: + file = os.Stdin + case syscall.STD_OUTPUT_HANDLE: + file = os.Stdout + case syscall.STD_ERROR_HANDLE: + file = os.Stderr + default: + panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile)) + } + } + + fd, err := syscall.GetStdHandle(nFile) + if err != nil { + panic(fmt.Errorf("Invalid standard handle identifier: %v -- %v", nFile, err)) + } + + return file, uintptr(fd) +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/api.go b/vendor/github.com/Azure/go-ansiterm/winterm/api.go new file mode 100644 index 00000000..6055e33b --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/api.go @@ -0,0 +1,327 @@ +// +build windows + +package winterm + +import ( + "fmt" + "syscall" + "unsafe" +) + +//=========================================================================================================== +// IMPORTANT NOTE: +// +// The methods below make extensive use of the "unsafe" package to obtain the required pointers. +// Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack +// variables) the pointers reference *before* the API completes. +// +// As a result, in those cases, the code must hint that the variables remain in active by invoking the +// dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer +// require unsafe pointers. +// +// If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform +// the garbage collector the variables remain in use if: +// +// -- The value is not a pointer (e.g., int32, struct) +// -- The value is not referenced by the method after passing the pointer to Windows +// +// See http://golang.org/doc/go1.3. +//=========================================================================================================== + +var ( + kernel32DLL = syscall.NewLazyDLL("kernel32.dll") + + getConsoleCursorInfoProc = kernel32DLL.NewProc("GetConsoleCursorInfo") + setConsoleCursorInfoProc = kernel32DLL.NewProc("SetConsoleCursorInfo") + setConsoleCursorPositionProc = kernel32DLL.NewProc("SetConsoleCursorPosition") + setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode") + getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo") + setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize") + scrollConsoleScreenBufferProc = kernel32DLL.NewProc("ScrollConsoleScreenBufferA") + setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute") + setConsoleWindowInfoProc = kernel32DLL.NewProc("SetConsoleWindowInfo") + writeConsoleOutputProc = kernel32DLL.NewProc("WriteConsoleOutputW") + readConsoleInputProc = kernel32DLL.NewProc("ReadConsoleInputW") + waitForSingleObjectProc = kernel32DLL.NewProc("WaitForSingleObject") +) + +// Windows Console constants +const ( + // Console modes + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx. + ENABLE_PROCESSED_INPUT = 0x0001 + ENABLE_LINE_INPUT = 0x0002 + ENABLE_ECHO_INPUT = 0x0004 + ENABLE_WINDOW_INPUT = 0x0008 + ENABLE_MOUSE_INPUT = 0x0010 + ENABLE_INSERT_MODE = 0x0020 + ENABLE_QUICK_EDIT_MODE = 0x0040 + ENABLE_EXTENDED_FLAGS = 0x0080 + ENABLE_AUTO_POSITION = 0x0100 + ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200 + + ENABLE_PROCESSED_OUTPUT = 0x0001 + ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002 + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + DISABLE_NEWLINE_AUTO_RETURN = 0x0008 + ENABLE_LVB_GRID_WORLDWIDE = 0x0010 + + // Character attributes + // Note: + // -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan). + // Clearing all foreground or background colors results in black; setting all creates white. + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes. + FOREGROUND_BLUE uint16 = 0x0001 + FOREGROUND_GREEN uint16 = 0x0002 + FOREGROUND_RED uint16 = 0x0004 + FOREGROUND_INTENSITY uint16 = 0x0008 + FOREGROUND_MASK uint16 = 0x000F + + BACKGROUND_BLUE uint16 = 0x0010 + BACKGROUND_GREEN uint16 = 0x0020 + BACKGROUND_RED uint16 = 0x0040 + BACKGROUND_INTENSITY uint16 = 0x0080 + BACKGROUND_MASK uint16 = 0x00F0 + + COMMON_LVB_MASK uint16 = 0xFF00 + COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000 + COMMON_LVB_UNDERSCORE uint16 = 0x8000 + + // Input event types + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx. + KEY_EVENT = 0x0001 + MOUSE_EVENT = 0x0002 + WINDOW_BUFFER_SIZE_EVENT = 0x0004 + MENU_EVENT = 0x0008 + FOCUS_EVENT = 0x0010 + + // WaitForSingleObject return codes + WAIT_ABANDONED = 0x00000080 + WAIT_FAILED = 0xFFFFFFFF + WAIT_SIGNALED = 0x0000000 + WAIT_TIMEOUT = 0x00000102 + + // WaitForSingleObject wait duration + WAIT_INFINITE = 0xFFFFFFFF + WAIT_ONE_SECOND = 1000 + WAIT_HALF_SECOND = 500 + WAIT_QUARTER_SECOND = 250 +) + +// Windows API Console types +// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD) +// -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment +type ( + CHAR_INFO struct { + UnicodeChar uint16 + Attributes uint16 + } + + CONSOLE_CURSOR_INFO struct { + Size uint32 + Visible int32 + } + + CONSOLE_SCREEN_BUFFER_INFO struct { + Size COORD + CursorPosition COORD + Attributes uint16 + Window SMALL_RECT + MaximumWindowSize COORD + } + + COORD struct { + X int16 + Y int16 + } + + SMALL_RECT struct { + Left int16 + Top int16 + Right int16 + Bottom int16 + } + + // INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx. + INPUT_RECORD struct { + EventType uint16 + KeyEvent KEY_EVENT_RECORD + } + + KEY_EVENT_RECORD struct { + KeyDown int32 + RepeatCount uint16 + VirtualKeyCode uint16 + VirtualScanCode uint16 + UnicodeChar uint16 + ControlKeyState uint32 + } + + WINDOW_BUFFER_SIZE struct { + Size COORD + } +) + +// boolToBOOL converts a Go bool into a Windows int32. +func boolToBOOL(f bool) int32 { + if f { + return int32(1) + } else { + return int32(0) + } +} + +// GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx. +func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error { + r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0) + return checkError(r1, r2, err) +} + +// SetConsoleCursorInfo sets the size and visiblity of the console cursor. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx. +func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error { + r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0) + return checkError(r1, r2, err) +} + +// SetConsoleCursorPosition location of the console cursor. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx. +func SetConsoleCursorPosition(handle uintptr, coord COORD) error { + r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord)) + use(coord) + return checkError(r1, r2, err) +} + +// GetConsoleMode gets the console mode for given file descriptor +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx. +func GetConsoleMode(handle uintptr) (mode uint32, err error) { + err = syscall.GetConsoleMode(syscall.Handle(handle), &mode) + return mode, err +} + +// SetConsoleMode sets the console mode for given file descriptor +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx. +func SetConsoleMode(handle uintptr, mode uint32) error { + r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0) + use(mode) + return checkError(r1, r2, err) +} + +// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer. +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx. +func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) { + info := CONSOLE_SCREEN_BUFFER_INFO{} + err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0)) + if err != nil { + return nil, err + } + return &info, nil +} + +func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error { + r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char))) + use(scrollRect) + use(clipRect) + use(destOrigin) + use(char) + return checkError(r1, r2, err) +} + +// SetConsoleScreenBufferSize sets the size of the console screen buffer. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx. +func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error { + r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord)) + use(coord) + return checkError(r1, r2, err) +} + +// SetConsoleTextAttribute sets the attributes of characters written to the +// console screen buffer by the WriteFile or WriteConsole function. +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx. +func SetConsoleTextAttribute(handle uintptr, attribute uint16) error { + r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0) + use(attribute) + return checkError(r1, r2, err) +} + +// SetConsoleWindowInfo sets the size and position of the console screen buffer's window. +// Note that the size and location must be within and no larger than the backing console screen buffer. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx. +func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error { + r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect))) + use(isAbsolute) + use(rect) + return checkError(r1, r2, err) +} + +// WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx. +func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error { + r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion))) + use(buffer) + use(bufferSize) + use(bufferCoord) + return checkError(r1, r2, err) +} + +// ReadConsoleInput reads (and removes) data from the console input buffer. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx. +func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error { + r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count))) + use(buffer) + return checkError(r1, r2, err) +} + +// WaitForSingleObject waits for the passed handle to be signaled. +// It returns true if the handle was signaled; false otherwise. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx. +func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) { + r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait))) + switch r1 { + case WAIT_ABANDONED, WAIT_TIMEOUT: + return false, nil + case WAIT_SIGNALED: + return true, nil + } + use(msWait) + return false, err +} + +// String helpers +func (info CONSOLE_SCREEN_BUFFER_INFO) String() string { + return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize) +} + +func (coord COORD) String() string { + return fmt.Sprintf("%v,%v", coord.X, coord.Y) +} + +func (rect SMALL_RECT) String() string { + return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom) +} + +// checkError evaluates the results of a Windows API call and returns the error if it failed. +func checkError(r1, r2 uintptr, err error) error { + // Windows APIs return non-zero to indicate success + if r1 != 0 { + return nil + } + + // Return the error if provided, otherwise default to EINVAL + if err != nil { + return err + } + return syscall.EINVAL +} + +// coordToPointer converts a COORD into a uintptr (by fooling the type system). +func coordToPointer(c COORD) uintptr { + // Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass. + return uintptr(*((*uint32)(unsafe.Pointer(&c)))) +} + +// use is a no-op, but the compiler cannot see that it is. +// Calling use(p) ensures that p is kept live until that point. +func use(p interface{}) {} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go b/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go new file mode 100644 index 00000000..cbec8f72 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go @@ -0,0 +1,100 @@ +// +build windows + +package winterm + +import "github.com/Azure/go-ansiterm" + +const ( + FOREGROUND_COLOR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE + BACKGROUND_COLOR_MASK = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE +) + +// collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the +// request represented by the passed ANSI mode. +func collectAnsiIntoWindowsAttributes(windowsMode uint16, inverted bool, baseMode uint16, ansiMode int16) (uint16, bool) { + switch ansiMode { + + // Mode styles + case ansiterm.ANSI_SGR_BOLD: + windowsMode = windowsMode | FOREGROUND_INTENSITY + + case ansiterm.ANSI_SGR_DIM, ansiterm.ANSI_SGR_BOLD_DIM_OFF: + windowsMode &^= FOREGROUND_INTENSITY + + case ansiterm.ANSI_SGR_UNDERLINE: + windowsMode = windowsMode | COMMON_LVB_UNDERSCORE + + case ansiterm.ANSI_SGR_REVERSE: + inverted = true + + case ansiterm.ANSI_SGR_REVERSE_OFF: + inverted = false + + case ansiterm.ANSI_SGR_UNDERLINE_OFF: + windowsMode &^= COMMON_LVB_UNDERSCORE + + // Foreground colors + case ansiterm.ANSI_SGR_FOREGROUND_DEFAULT: + windowsMode = (windowsMode &^ FOREGROUND_MASK) | (baseMode & FOREGROUND_MASK) + + case ansiterm.ANSI_SGR_FOREGROUND_BLACK: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) + + case ansiterm.ANSI_SGR_FOREGROUND_RED: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED + + case ansiterm.ANSI_SGR_FOREGROUND_GREEN: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN + + case ansiterm.ANSI_SGR_FOREGROUND_YELLOW: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN + + case ansiterm.ANSI_SGR_FOREGROUND_BLUE: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_BLUE + + case ansiterm.ANSI_SGR_FOREGROUND_MAGENTA: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_BLUE + + case ansiterm.ANSI_SGR_FOREGROUND_CYAN: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE + + case ansiterm.ANSI_SGR_FOREGROUND_WHITE: + windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE + + // Background colors + case ansiterm.ANSI_SGR_BACKGROUND_DEFAULT: + // Black with no intensity + windowsMode = (windowsMode &^ BACKGROUND_MASK) | (baseMode & BACKGROUND_MASK) + + case ansiterm.ANSI_SGR_BACKGROUND_BLACK: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) + + case ansiterm.ANSI_SGR_BACKGROUND_RED: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED + + case ansiterm.ANSI_SGR_BACKGROUND_GREEN: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN + + case ansiterm.ANSI_SGR_BACKGROUND_YELLOW: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN + + case ansiterm.ANSI_SGR_BACKGROUND_BLUE: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_BLUE + + case ansiterm.ANSI_SGR_BACKGROUND_MAGENTA: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_BLUE + + case ansiterm.ANSI_SGR_BACKGROUND_CYAN: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE + + case ansiterm.ANSI_SGR_BACKGROUND_WHITE: + windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE + } + + return windowsMode, inverted +} + +// invertAttributes inverts the foreground and background colors of a Windows attributes value +func invertAttributes(windowsMode uint16) uint16 { + return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4) +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go b/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go new file mode 100644 index 00000000..3ee06ea7 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go @@ -0,0 +1,101 @@ +// +build windows + +package winterm + +const ( + horizontal = iota + vertical +) + +func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT { + if h.originMode { + sr := h.effectiveSr(info.Window) + return SMALL_RECT{ + Top: sr.top, + Bottom: sr.bottom, + Left: 0, + Right: info.Size.X - 1, + } + } else { + return SMALL_RECT{ + Top: info.Window.Top, + Bottom: info.Window.Bottom, + Left: 0, + Right: info.Size.X - 1, + } + } +} + +// setCursorPosition sets the cursor to the specified position, bounded to the screen size +func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error { + position.X = ensureInRange(position.X, window.Left, window.Right) + position.Y = ensureInRange(position.Y, window.Top, window.Bottom) + err := SetConsoleCursorPosition(h.fd, position) + if err != nil { + return err + } + h.logf("Cursor position set: (%d, %d)", position.X, position.Y) + return err +} + +func (h *windowsAnsiEventHandler) moveCursorVertical(param int) error { + return h.moveCursor(vertical, param) +} + +func (h *windowsAnsiEventHandler) moveCursorHorizontal(param int) error { + return h.moveCursor(horizontal, param) +} + +func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + position := info.CursorPosition + switch moveMode { + case horizontal: + position.X += int16(param) + case vertical: + position.Y += int16(param) + } + + if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) moveCursorLine(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + position := info.CursorPosition + position.X = 0 + position.Y += int16(param) + + if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) moveCursorColumn(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + position := info.CursorPosition + position.X = int16(param) - 1 + + if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go b/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go new file mode 100644 index 00000000..244b5fa2 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go @@ -0,0 +1,84 @@ +// +build windows + +package winterm + +import "github.com/Azure/go-ansiterm" + +func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD, toCoord COORD) error { + // Ignore an invalid (negative area) request + if toCoord.Y < fromCoord.Y { + return nil + } + + var err error + + var coordStart = COORD{} + var coordEnd = COORD{} + + xCurrent, yCurrent := fromCoord.X, fromCoord.Y + xEnd, yEnd := toCoord.X, toCoord.Y + + // Clear any partial initial line + if xCurrent > 0 { + coordStart.X, coordStart.Y = xCurrent, yCurrent + coordEnd.X, coordEnd.Y = xEnd, yCurrent + + err = h.clearRect(attributes, coordStart, coordEnd) + if err != nil { + return err + } + + xCurrent = 0 + yCurrent += 1 + } + + // Clear intervening rectangular section + if yCurrent < yEnd { + coordStart.X, coordStart.Y = xCurrent, yCurrent + coordEnd.X, coordEnd.Y = xEnd, yEnd-1 + + err = h.clearRect(attributes, coordStart, coordEnd) + if err != nil { + return err + } + + xCurrent = 0 + yCurrent = yEnd + } + + // Clear remaining partial ending line + coordStart.X, coordStart.Y = xCurrent, yCurrent + coordEnd.X, coordEnd.Y = xEnd, yEnd + + err = h.clearRect(attributes, coordStart, coordEnd) + if err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD, toCoord COORD) error { + region := SMALL_RECT{Top: fromCoord.Y, Left: fromCoord.X, Bottom: toCoord.Y, Right: toCoord.X} + width := toCoord.X - fromCoord.X + 1 + height := toCoord.Y - fromCoord.Y + 1 + size := uint32(width) * uint32(height) + + if size <= 0 { + return nil + } + + buffer := make([]CHAR_INFO, size) + + char := CHAR_INFO{ansiterm.FILL_CHARACTER, attributes} + for i := 0; i < int(size); i++ { + buffer[i] = char + } + + err := WriteConsoleOutput(h.fd, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, ®ion) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go b/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go new file mode 100644 index 00000000..2d27fa1d --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go @@ -0,0 +1,118 @@ +// +build windows + +package winterm + +// effectiveSr gets the current effective scroll region in buffer coordinates +func (h *windowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion { + top := addInRange(window.Top, h.sr.top, window.Top, window.Bottom) + bottom := addInRange(window.Top, h.sr.bottom, window.Top, window.Bottom) + if top >= bottom { + top = window.Top + bottom = window.Bottom + } + return scrollRegion{top: top, bottom: bottom} +} + +func (h *windowsAnsiEventHandler) scrollUp(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + sr := h.effectiveSr(info.Window) + return h.scroll(param, sr, info) +} + +func (h *windowsAnsiEventHandler) scrollDown(param int) error { + return h.scrollUp(-param) +} + +func (h *windowsAnsiEventHandler) deleteLines(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + start := info.CursorPosition.Y + sr := h.effectiveSr(info.Window) + // Lines cannot be inserted or deleted outside the scrolling region. + if start >= sr.top && start <= sr.bottom { + sr.top = start + return h.scroll(param, sr, info) + } else { + return nil + } +} + +func (h *windowsAnsiEventHandler) insertLines(param int) error { + return h.deleteLines(-param) +} + +// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates. +func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error { + h.logf("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom) + h.logf("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom) + + // Copy from and clip to the scroll region (full buffer width) + scrollRect := SMALL_RECT{ + Top: sr.top, + Bottom: sr.bottom, + Left: 0, + Right: info.Size.X - 1, + } + + // Origin to which area should be copied + destOrigin := COORD{ + X: 0, + Y: sr.top - int16(param), + } + + char := CHAR_INFO{ + UnicodeChar: ' ', + Attributes: h.attributes, + } + + if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil { + return err + } + return nil +} + +func (h *windowsAnsiEventHandler) deleteCharacters(param int) error { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + return h.scrollLine(param, info.CursorPosition, info) +} + +func (h *windowsAnsiEventHandler) insertCharacters(param int) error { + return h.deleteCharacters(-param) +} + +// scrollLine scrolls a line horizontally starting at the provided position by a number of columns. +func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error { + // Copy from and clip to the scroll region (full buffer width) + scrollRect := SMALL_RECT{ + Top: position.Y, + Bottom: position.Y, + Left: position.X, + Right: info.Size.X - 1, + } + + // Origin to which area should be copied + destOrigin := COORD{ + X: position.X - int16(columns), + Y: position.Y, + } + + char := CHAR_INFO{ + UnicodeChar: ' ', + Attributes: h.attributes, + } + + if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go b/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go new file mode 100644 index 00000000..afa7635d --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go @@ -0,0 +1,9 @@ +// +build windows + +package winterm + +// AddInRange increments a value by the passed quantity while ensuring the values +// always remain within the supplied min / max range. +func addInRange(n int16, increment int16, min int16, max int16) int16 { + return ensureInRange(n+increment, min, max) +} diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go b/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go new file mode 100644 index 00000000..2d40fb75 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go @@ -0,0 +1,743 @@ +// +build windows + +package winterm + +import ( + "bytes" + "log" + "os" + "strconv" + + "github.com/Azure/go-ansiterm" +) + +type windowsAnsiEventHandler struct { + fd uintptr + file *os.File + infoReset *CONSOLE_SCREEN_BUFFER_INFO + sr scrollRegion + buffer bytes.Buffer + attributes uint16 + inverted bool + wrapNext bool + drewMarginByte bool + originMode bool + marginByte byte + curInfo *CONSOLE_SCREEN_BUFFER_INFO + curPos COORD + logf func(string, ...interface{}) +} + +type Option func(*windowsAnsiEventHandler) + +func WithLogf(f func(string, ...interface{})) Option { + return func(w *windowsAnsiEventHandler) { + w.logf = f + } +} + +func CreateWinEventHandler(fd uintptr, file *os.File, opts ...Option) ansiterm.AnsiEventHandler { + infoReset, err := GetConsoleScreenBufferInfo(fd) + if err != nil { + return nil + } + + h := &windowsAnsiEventHandler{ + fd: fd, + file: file, + infoReset: infoReset, + attributes: infoReset.Attributes, + } + for _, o := range opts { + o(h) + } + + if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { + logFile, _ := os.Create("winEventHandler.log") + logger := log.New(logFile, "", log.LstdFlags) + if h.logf != nil { + l := h.logf + h.logf = func(s string, v ...interface{}) { + l(s, v...) + logger.Printf(s, v...) + } + } else { + h.logf = logger.Printf + } + } + + if h.logf == nil { + h.logf = func(string, ...interface{}) {} + } + + return h +} + +type scrollRegion struct { + top int16 + bottom int16 +} + +// simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the +// current cursor position and scroll region settings, in which case it returns +// true. If no special handling is necessary, then it does nothing and returns +// false. +// +// In the false case, the caller should ensure that a carriage return +// and line feed are inserted or that the text is otherwise wrapped. +func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) { + if h.wrapNext { + if err := h.Flush(); err != nil { + return false, err + } + h.clearWrap() + } + pos, info, err := h.getCurrentInfo() + if err != nil { + return false, err + } + sr := h.effectiveSr(info.Window) + if pos.Y == sr.bottom { + // Scrolling is necessary. Let Windows automatically scroll if the scrolling region + // is the full window. + if sr.top == info.Window.Top && sr.bottom == info.Window.Bottom { + if includeCR { + pos.X = 0 + h.updatePos(pos) + } + return false, nil + } + + // A custom scroll region is active. Scroll the window manually to simulate + // the LF. + if err := h.Flush(); err != nil { + return false, err + } + h.logf("Simulating LF inside scroll region") + if err := h.scrollUp(1); err != nil { + return false, err + } + if includeCR { + pos.X = 0 + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return false, err + } + } + return true, nil + + } else if pos.Y < info.Window.Bottom { + // Let Windows handle the LF. + pos.Y++ + if includeCR { + pos.X = 0 + } + h.updatePos(pos) + return false, nil + } else { + // The cursor is at the bottom of the screen but outside the scroll + // region. Skip the LF. + h.logf("Simulating LF outside scroll region") + if includeCR { + if err := h.Flush(); err != nil { + return false, err + } + pos.X = 0 + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return false, err + } + } + return true, nil + } +} + +// executeLF executes a LF without a CR. +func (h *windowsAnsiEventHandler) executeLF() error { + handled, err := h.simulateLF(false) + if err != nil { + return err + } + if !handled { + // Windows LF will reset the cursor column position. Write the LF + // and restore the cursor position. + pos, _, err := h.getCurrentInfo() + if err != nil { + return err + } + h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED) + if pos.X != 0 { + if err := h.Flush(); err != nil { + return err + } + h.logf("Resetting cursor position for LF without CR") + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return err + } + } + } + return nil +} + +func (h *windowsAnsiEventHandler) Print(b byte) error { + if h.wrapNext { + h.buffer.WriteByte(h.marginByte) + h.clearWrap() + if _, err := h.simulateLF(true); err != nil { + return err + } + } + pos, info, err := h.getCurrentInfo() + if err != nil { + return err + } + if pos.X == info.Size.X-1 { + h.wrapNext = true + h.marginByte = b + } else { + pos.X++ + h.updatePos(pos) + h.buffer.WriteByte(b) + } + return nil +} + +func (h *windowsAnsiEventHandler) Execute(b byte) error { + switch b { + case ansiterm.ANSI_TAB: + h.logf("Execute(TAB)") + // Move to the next tab stop, but preserve auto-wrap if already set. + if !h.wrapNext { + pos, info, err := h.getCurrentInfo() + if err != nil { + return err + } + pos.X = (pos.X + 8) - pos.X%8 + if pos.X >= info.Size.X { + pos.X = info.Size.X - 1 + } + if err := h.Flush(); err != nil { + return err + } + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return err + } + } + return nil + + case ansiterm.ANSI_BEL: + h.buffer.WriteByte(ansiterm.ANSI_BEL) + return nil + + case ansiterm.ANSI_BACKSPACE: + if h.wrapNext { + if err := h.Flush(); err != nil { + return err + } + h.clearWrap() + } + pos, _, err := h.getCurrentInfo() + if err != nil { + return err + } + if pos.X > 0 { + pos.X-- + h.updatePos(pos) + h.buffer.WriteByte(ansiterm.ANSI_BACKSPACE) + } + return nil + + case ansiterm.ANSI_VERTICAL_TAB, ansiterm.ANSI_FORM_FEED: + // Treat as true LF. + return h.executeLF() + + case ansiterm.ANSI_LINE_FEED: + // Simulate a CR and LF for now since there is no way in go-ansiterm + // to tell if the LF should include CR (and more things break when it's + // missing than when it's incorrectly added). + handled, err := h.simulateLF(true) + if handled || err != nil { + return err + } + return h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED) + + case ansiterm.ANSI_CARRIAGE_RETURN: + if h.wrapNext { + if err := h.Flush(); err != nil { + return err + } + h.clearWrap() + } + pos, _, err := h.getCurrentInfo() + if err != nil { + return err + } + if pos.X != 0 { + pos.X = 0 + h.updatePos(pos) + h.buffer.WriteByte(ansiterm.ANSI_CARRIAGE_RETURN) + } + return nil + + default: + return nil + } +} + +func (h *windowsAnsiEventHandler) CUU(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUU: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorVertical(-param) +} + +func (h *windowsAnsiEventHandler) CUD(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUD: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorVertical(param) +} + +func (h *windowsAnsiEventHandler) CUF(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUF: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorHorizontal(param) +} + +func (h *windowsAnsiEventHandler) CUB(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUB: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorHorizontal(-param) +} + +func (h *windowsAnsiEventHandler) CNL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CNL: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorLine(param) +} + +func (h *windowsAnsiEventHandler) CPL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CPL: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorLine(-param) +} + +func (h *windowsAnsiEventHandler) CHA(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CHA: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.moveCursorColumn(param) +} + +func (h *windowsAnsiEventHandler) VPA(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("VPA: [[%d]]", param) + h.clearWrap() + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + window := h.getCursorWindow(info) + position := info.CursorPosition + position.Y = window.Top + int16(param) - 1 + return h.setCursorPosition(position, window) +} + +func (h *windowsAnsiEventHandler) CUP(row int, col int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("CUP: [[%d %d]]", row, col) + h.clearWrap() + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + window := h.getCursorWindow(info) + position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1} + return h.setCursorPosition(position, window) +} + +func (h *windowsAnsiEventHandler) HVP(row int, col int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("HVP: [[%d %d]]", row, col) + h.clearWrap() + return h.CUP(row, col) +} + +func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DECTCEM: [%v]", []string{strconv.FormatBool(visible)}) + h.clearWrap() + return nil +} + +func (h *windowsAnsiEventHandler) DECOM(enable bool) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DECOM: [%v]", []string{strconv.FormatBool(enable)}) + h.clearWrap() + h.originMode = enable + return h.CUP(1, 1) +} + +func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DECCOLM: [%v]", []string{strconv.FormatBool(use132)}) + h.clearWrap() + if err := h.ED(2); err != nil { + return err + } + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + targetWidth := int16(80) + if use132 { + targetWidth = 132 + } + if info.Size.X < targetWidth { + if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { + h.logf("set buffer failed: %v", err) + return err + } + } + window := info.Window + window.Left = 0 + window.Right = targetWidth - 1 + if err := SetConsoleWindowInfo(h.fd, true, window); err != nil { + h.logf("set window failed: %v", err) + return err + } + if info.Size.X > targetWidth { + if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { + h.logf("set buffer failed: %v", err) + return err + } + } + return SetConsoleCursorPosition(h.fd, COORD{0, 0}) +} + +func (h *windowsAnsiEventHandler) ED(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("ED: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + + // [J -- Erases from the cursor to the end of the screen, including the cursor position. + // [1J -- Erases from the beginning of the screen to the cursor, including the cursor position. + // [2J -- Erases the complete display. The cursor does not move. + // Notes: + // -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles + + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + var start COORD + var end COORD + + switch param { + case 0: + start = info.CursorPosition + end = COORD{info.Size.X - 1, info.Size.Y - 1} + + case 1: + start = COORD{0, 0} + end = info.CursorPosition + + case 2: + start = COORD{0, 0} + end = COORD{info.Size.X - 1, info.Size.Y - 1} + } + + err = h.clearRange(h.attributes, start, end) + if err != nil { + return err + } + + // If the whole buffer was cleared, move the window to the top while preserving + // the window-relative cursor position. + if param == 2 { + pos := info.CursorPosition + window := info.Window + pos.Y -= window.Top + window.Bottom -= window.Top + window.Top = 0 + if err := SetConsoleCursorPosition(h.fd, pos); err != nil { + return err + } + if err := SetConsoleWindowInfo(h.fd, true, window); err != nil { + return err + } + } + + return nil +} + +func (h *windowsAnsiEventHandler) EL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("EL: [%v]", strconv.Itoa(param)) + h.clearWrap() + + // [K -- Erases from the cursor to the end of the line, including the cursor position. + // [1K -- Erases from the beginning of the line to the cursor, including the cursor position. + // [2K -- Erases the complete line. + + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + var start COORD + var end COORD + + switch param { + case 0: + start = info.CursorPosition + end = COORD{info.Size.X, info.CursorPosition.Y} + + case 1: + start = COORD{0, info.CursorPosition.Y} + end = info.CursorPosition + + case 2: + start = COORD{0, info.CursorPosition.Y} + end = COORD{info.Size.X, info.CursorPosition.Y} + } + + err = h.clearRange(h.attributes, start, end) + if err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) IL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("IL: [%v]", strconv.Itoa(param)) + h.clearWrap() + return h.insertLines(param) +} + +func (h *windowsAnsiEventHandler) DL(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DL: [%v]", strconv.Itoa(param)) + h.clearWrap() + return h.deleteLines(param) +} + +func (h *windowsAnsiEventHandler) ICH(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("ICH: [%v]", strconv.Itoa(param)) + h.clearWrap() + return h.insertCharacters(param) +} + +func (h *windowsAnsiEventHandler) DCH(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DCH: [%v]", strconv.Itoa(param)) + h.clearWrap() + return h.deleteCharacters(param) +} + +func (h *windowsAnsiEventHandler) SGR(params []int) error { + if err := h.Flush(); err != nil { + return err + } + strings := []string{} + for _, v := range params { + strings = append(strings, strconv.Itoa(v)) + } + + h.logf("SGR: [%v]", strings) + + if len(params) <= 0 { + h.attributes = h.infoReset.Attributes + h.inverted = false + } else { + for _, attr := range params { + + if attr == ansiterm.ANSI_SGR_RESET { + h.attributes = h.infoReset.Attributes + h.inverted = false + continue + } + + h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, int16(attr)) + } + } + + attributes := h.attributes + if h.inverted { + attributes = invertAttributes(attributes) + } + err := SetConsoleTextAttribute(h.fd, attributes) + if err != nil { + return err + } + + return nil +} + +func (h *windowsAnsiEventHandler) SU(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("SU: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.scrollUp(param) +} + +func (h *windowsAnsiEventHandler) SD(param int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("SD: [%v]", []string{strconv.Itoa(param)}) + h.clearWrap() + return h.scrollDown(param) +} + +func (h *windowsAnsiEventHandler) DA(params []string) error { + h.logf("DA: [%v]", params) + // DA cannot be implemented because it must send data on the VT100 input stream, + // which is not available to go-ansiterm. + return nil +} + +func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error { + if err := h.Flush(); err != nil { + return err + } + h.logf("DECSTBM: [%d, %d]", top, bottom) + + // Windows is 0 indexed, Linux is 1 indexed + h.sr.top = int16(top - 1) + h.sr.bottom = int16(bottom - 1) + + // This command also moves the cursor to the origin. + h.clearWrap() + return h.CUP(1, 1) +} + +func (h *windowsAnsiEventHandler) RI() error { + if err := h.Flush(); err != nil { + return err + } + h.logf("RI: []") + h.clearWrap() + + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + sr := h.effectiveSr(info.Window) + if info.CursorPosition.Y == sr.top { + return h.scrollDown(1) + } + + return h.moveCursorVertical(-1) +} + +func (h *windowsAnsiEventHandler) IND() error { + h.logf("IND: []") + return h.executeLF() +} + +func (h *windowsAnsiEventHandler) Flush() error { + h.curInfo = nil + if h.buffer.Len() > 0 { + h.logf("Flush: [%s]", h.buffer.Bytes()) + if _, err := h.buffer.WriteTo(h.file); err != nil { + return err + } + } + + if h.wrapNext && !h.drewMarginByte { + h.logf("Flush: drawing margin byte '%c'", h.marginByte) + + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return err + } + + charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}} + size := COORD{1, 1} + position := COORD{0, 0} + region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y} + if err := WriteConsoleOutput(h.fd, charInfo, size, position, ®ion); err != nil { + return err + } + h.drewMarginByte = true + } + return nil +} + +// cacheConsoleInfo ensures that the current console screen information has been queried +// since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos. +func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) { + if h.curInfo == nil { + info, err := GetConsoleScreenBufferInfo(h.fd) + if err != nil { + return COORD{}, nil, err + } + h.curInfo = info + h.curPos = info.CursorPosition + } + return h.curPos, h.curInfo, nil +} + +func (h *windowsAnsiEventHandler) updatePos(pos COORD) { + if h.curInfo == nil { + panic("failed to call getCurrentInfo before calling updatePos") + } + h.curPos = pos +} + +// clearWrap clears the state where the cursor is in the margin +// waiting for the next character before wrapping the line. This must +// be done before most operations that act on the cursor. +func (h *windowsAnsiEventHandler) clearWrap() { + h.wrapNext = false + h.drewMarginByte = false +} diff --git a/vendor/github.com/docker/docker/pkg/term/deprecated.go b/vendor/github.com/docker/docker/pkg/term/deprecated.go new file mode 100644 index 00000000..4d18168f --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/deprecated.go @@ -0,0 +1,84 @@ +// Package term provides structures and helper functions to work with +// terminal (state, sizes). +// +// Deprecated: use github.com/moby/term instead +package term // import "github.com/docker/docker/pkg/term" + +import ( + "github.com/moby/term" +) + +// EscapeError is special error which returned by a TTY proxy reader's Read() +// method in case its detach escape sequence is read. +// Deprecated: use github.com/moby/term.EscapeError +type EscapeError = term.EscapeError + +// State represents the state of the terminal. +// Deprecated: use github.com/moby/term.State +type State = term.State + +// Winsize represents the size of the terminal window. +// Deprecated: use github.com/moby/term.Winsize +type Winsize = term.Winsize + +var ( + // ASCII list the possible supported ASCII key sequence + ASCII = term.ASCII + + // ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code. + // Deprecated: use github.com/moby/term.ToBytes + ToBytes = term.ToBytes + + // StdStreams returns the standard streams (stdin, stdout, stderr). + // Deprecated: use github.com/moby/term.StdStreams + StdStreams = term.StdStreams + + // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. + // Deprecated: use github.com/moby/term.GetFdInfo + GetFdInfo = term.GetFdInfo + + // GetWinsize returns the window size based on the specified file descriptor. + // Deprecated: use github.com/moby/term.GetWinsize + GetWinsize = term.GetWinsize + + // IsTerminal returns true if the given file descriptor is a terminal. + // Deprecated: use github.com/moby/term.IsTerminal + IsTerminal = term.IsTerminal + + // RestoreTerminal restores the terminal connected to the given file descriptor + // to a previous state. + // Deprecated: use github.com/moby/term.RestoreTerminal + RestoreTerminal = term.RestoreTerminal + + // SaveState saves the state of the terminal connected to the given file descriptor. + // Deprecated: use github.com/moby/term.SaveState + SaveState = term.SaveState + + // DisableEcho applies the specified state to the terminal connected to the file + // descriptor, with echo disabled. + // Deprecated: use github.com/moby/term.DisableEcho + DisableEcho = term.DisableEcho + + // SetRawTerminal puts the terminal connected to the given file descriptor into + // raw mode and returns the previous state. On UNIX, this puts both the input + // and output into raw mode. On Windows, it only puts the input into raw mode. + // Deprecated: use github.com/moby/term.SetRawTerminal + SetRawTerminal = term.SetRawTerminal + + // SetRawTerminalOutput puts the output of terminal connected to the given file + // descriptor into raw mode. On UNIX, this does nothing and returns nil for the + // state. On Windows, it disables LF -> CRLF translation. + // Deprecated: use github.com/moby/term.SetRawTerminalOutput + SetRawTerminalOutput = term.SetRawTerminalOutput + + // MakeRaw puts the terminal connected to the given file descriptor into raw + // mode and returns the previous state of the terminal so that it can be restored. + // Deprecated: use github.com/moby/term.MakeRaw + MakeRaw = term.MakeRaw + + // NewEscapeProxy returns a new TTY proxy reader which wraps the given reader + // and detects when the specified escape keys are read, in which case the Read + // method will return an error of type EscapeError. + // Deprecated: use github.com/moby/term.NewEscapeProxy + NewEscapeProxy = term.NewEscapeProxy +) diff --git a/vendor/github.com/docker/docker/pkg/term/deprecated_unix.go b/vendor/github.com/docker/docker/pkg/term/deprecated_unix.go new file mode 100644 index 00000000..2d3641dc --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/deprecated_unix.go @@ -0,0 +1,21 @@ +//go:build !windows +// +build !windows + +package term // import "github.com/docker/docker/pkg/term" + +import ( + "github.com/moby/term" +) + +// Termios is the Unix API for terminal I/O. +// Deprecated: use github.com/moby/term.Termios +type Termios = term.Termios + +var ( + // ErrInvalidState is returned if the state of the terminal is invalid. + ErrInvalidState = term.ErrInvalidState + + // SetWinsize tries to set the specified window size for the specified file descriptor. + // Deprecated: use github.com/moby/term.GetWinsize + SetWinsize = term.SetWinsize +) diff --git a/vendor/github.com/micmonay/keybd_event/LICENSE b/vendor/github.com/micmonay/keybd_event/LICENSE new file mode 100644 index 00000000..0464c437 --- /dev/null +++ b/vendor/github.com/micmonay/keybd_event/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2016 MicMonay + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/micmonay/keybd_event/README.md b/vendor/github.com/micmonay/keybd_event/README.md new file mode 100644 index 00000000..3aa9b2c3 --- /dev/null +++ b/vendor/github.com/micmonay/keybd_event/README.md @@ -0,0 +1,77 @@ +# keybd_event + +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/micmonay/keybd_event) + +This library simulates the key press on a keyboard. It runs on Linux, Windows and Mac. + +**Important :** +- The keys change in the different keyboard layouts of the target computer(s). +- I have tested this code on my system and I don't find any errors. If you have a bug, please create an issue. + + +### Example : +```go +package main + +import ( + "runtime" + "time" + "github.com/micmonay/keybd_event" +) + +func main() { + kb, err := keybd_event.NewKeyBonding() + if err != nil { + panic(err) + } + + // For linux, it is very important to wait 2 seconds + if runtime.GOOS == "linux" { + time.Sleep(2 * time.Second) + } + + // Select keys to be pressed + kb.SetKeys(keybd_event.VK_A, keybd_event.VK_B) + + // Set shift to be pressed + kb.HasSHIFT(true) + + // Press the selected keys + err = kb.Launching() + if err != nil { + panic(err) + } + + // Or you can use Press and Release + kb.Press() + time.Sleep(10 * time.Millisecond) + kb.Release() + + // Here, the program will generate "ABAB" as if they were pressed on the keyboard. +} +``` + +For easy access to all the keys on the virtual keyboard, I have added more special keycodes constants `VK_SP*`. + +Below is an illustration showing the "VK_" symbols for each key on the keyboard: +![keyboard.png](./keyboard.png) + +## Linux + +On Linux this library uses **uinput**, which on the major distributions requires root permissions. + +The easy solution is executing through root user (by using `sudo`). A worse way is by changing the executable's permissions by using `chmod`. + +### Secure Linux Example +```bash +sudo groupadd uinput +sudo usermod -a -G uinput my_username +sudo udevadm control --reload-rules +echo "SUBSYSTEM==\"misc\", KERNEL==\"uinput\", GROUP=\"uinput\", MODE=\"0660\"" | sudo tee /etc/udev/rules.d/uinput.rules +echo uinput | sudo tee /etc/modules-load.d/uinput.conf +``` + +Another subtlety on Linux: it is important after creating the `keybd_event` to **wait 2 seconds before running first keyboard actions**. + +## Darwin (MacOS) +This library depends on Apple's frameworks, and I did not find a solution to cross-compile from another OS to MacOS. diff --git a/vendor/github.com/micmonay/keybd_event/keybd_darwin.go b/vendor/github.com/micmonay/keybd_event/keybd_darwin.go new file mode 100644 index 00000000..154de575 --- /dev/null +++ b/vendor/github.com/micmonay/keybd_event/keybd_darwin.go @@ -0,0 +1,262 @@ +package keybd_event + +/* + #cgo CFLAGS: -x objective-c + #cgo LDFLAGS: -framework Cocoa + #import + CGEventRef CreateDown(int k){ + CGEventRef event = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)k, true); + return event; + } + CGEventRef CreateUp(int k){ + CGEventRef event = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)k, false); + return event; + } + void KeyTap(CGEventRef event){ + CGEventPost(kCGAnnotatedSessionEventTap, event); + CFRelease(event); + } + void AddActionKey(CGEventFlags type,CGEventRef event){ + CGEventSetFlags(event, type); + } +*/ +import "C" +import "time" + +const ( + _AShift = C.kCGEventFlagMaskAlphaShift + _VK_SHIFT = C.kCGEventFlagMaskShift + _VK_CTRL = C.kCGEventFlagMaskControl + _VK_ALT = C.kCGEventFlagMaskAlternate + _VK_CMD = C.kCGEventFlagMaskCommand + _Help = C.kCGEventFlagMaskHelp + _VK_FN = C.kCGEventFlagMaskSecondaryFn + _NumPad = C.kCGEventFlagMaskNumericPad + _Coalesced = C.kCGEventFlagMaskNonCoalesced + _VK_Control = 0x3B + _VK_RightShift = 0x3C + _VK_RightControl = 0x3E + _VK_Command = 0x37 + _VK_Shift = 0x38 +) + +func initKeyBD() error { return nil } + +// Press key(s) +func (k *KeyBonding) Press() error { + for _, key := range k.keys { + k.keyPress(key) + } + return nil +} + +// Release key(s) +func (k *KeyBonding) Release() error { + for _, key := range k.keys { + k.keyRelease(key) + } + return nil +} + +// Launch key bounding +func (k *KeyBonding) Launching() error { + + for _, key := range k.keys { + k.tapKey(key) + } + return nil +} +func altgr(event C.CGEventRef) { + alt(event) +} +func shift(event C.CGEventRef) { + C.AddActionKey(_VK_SHIFT, event) +} +func ctrl(event C.CGEventRef) { + C.AddActionKey(_VK_CTRL, event) +} +func alt(event C.CGEventRef) { + C.AddActionKey(_VK_ALT, event) +} +func cmd(event C.CGEventRef) { + C.AddActionKey(_VK_CMD, event) +} +func (k KeyBonding) keyPress(key int) { + downEvent := C.CreateDown(C.int(key)) + if k.hasALT { + alt(downEvent) + } + if k.hasCTRL { + ctrl(downEvent) + } + if k.hasSHIFT { + shift(downEvent) + } + if k.hasRCTRL { //not support on mac + ctrl(downEvent) + } + if k.hasRSHIFT { //not support on mac + shift(downEvent) + } + if k.hasALTGR { + altgr(downEvent) + } + if k.hasSuper { + cmd(downEvent) + } + C.KeyTap(downEvent) +} +func (k KeyBonding) keyRelease(key int) { + upEvent := C.CreateUp(C.int(key)) + if k.hasALT { + alt(upEvent) + } + if k.hasCTRL { + ctrl(upEvent) + } + if k.hasSHIFT { + shift(upEvent) + } + if k.hasRCTRL { //not support on mac + ctrl(upEvent) + } + if k.hasRSHIFT { //not support on mac + shift(upEvent) + } + if k.hasALTGR { + altgr(upEvent) + } + if k.hasSuper { + cmd(upEvent) + } + C.KeyTap(upEvent) +} +func (k KeyBonding) tapKey(key int) { + k.keyPress(key) + time.Sleep(100 * time.Millisecond) //ignore if speed is most in my test system + k.keyRelease(key) +} + +const ( + VK_SP1 = 0x0A + VK_SP2 = 0x1B + VK_SP3 = 0x18 + VK_SP4 = 0x21 + VK_SP5 = 0x1E + VK_SP6 = 0x29 + VK_SP7 = 0x27 + VK_SP8 = 0x2A + VK_SP9 = 0x2B + VK_SP10 = 0x2F + VK_SP11 = 0x2C + VK_SP12 = 0x32 + + VK_A = 0x00 + VK_S = 0x01 + VK_D = 0x02 + VK_F = 0x03 + VK_H = 0x04 + VK_G = 0x05 + VK_Z = 0x06 + VK_X = 0x07 + VK_C = 0x08 + VK_V = 0x09 + VK_B = 0x0B + VK_Q = 0x0C + VK_W = 0x0D + VK_E = 0x0E + VK_R = 0x0F + VK_Y = 0x10 + VK_T = 0x11 + VK_1 = 0x12 + VK_2 = 0x13 + VK_3 = 0x14 + VK_4 = 0x15 + VK_6 = 0x16 + VK_5 = 0x17 + VK_EQUAL = 0x18 + VK_9 = 0x19 + VK_7 = 0x1A + VK_MINUS = 0x1B + VK_8 = 0x1C + VK_0 = 0x1D + VK_RightBracket = 0x1E + VK_O = 0x1F + VK_U = 0x20 + VK_LeftBracket = 0x21 + VK_I = 0x22 + VK_P = 0x23 + VK_L = 0x25 + VK_J = 0x26 + VK_Quote = 0x27 + VK_K = 0x28 + VK_SEMICOLON = 0x29 + VK_BACKSLASH = 0x2A + VK_COMMA = 0x2B + VK_SLASH = 0x2C + VK_N = 0x2D + VK_M = 0x2E + VK_Period = 0x2F + VK_GRAVE = 0x32 + VK_KeypadDecimal = 0x41 + VK_KeypadMultiply = 0x43 + VK_KeypadPlus = 0x45 + VK_KeypadClear = 0x47 + VK_KeypadDivide = 0x4B + VK_KeypadEnter = 0x4C + VK_KeypadMinus = 0x4E + VK_KeypadEquals = 0x51 + VK_Keypad0 = 0x52 + VK_Keypad1 = 0x53 + VK_Keypad2 = 0x54 + VK_Keypad3 = 0x55 + VK_Keypad4 = 0x56 + VK_Keypad5 = 0x57 + VK_Keypad6 = 0x58 + VK_Keypad7 = 0x59 + VK_Keypad8 = 0x5B + VK_Keypad9 = 0x5C + + VK_ENTER = 0x24 + VK_TAB = 0x30 + VK_SPACE = 0x31 + VK_DELETE = 0x33 + VK_ESC = 0x35 + VK_CAPSLOCK = 0x39 + VK_Option = 0x3A + VK_RightOption = 0x3D + VK_Function = 0x3F + VK_F17 = 0x40 + VK_VOLUMEUP = 0x48 + VK_VOLUMEDOWN = 0x49 + VK_MUTE = 0x4A + VK_F18 = 0x4F + VK_F19 = 0x50 + VK_F20 = 0x5A + VK_F5 = 0x60 + VK_F6 = 0x61 + VK_F7 = 0x62 + VK_F3 = 0x63 + VK_F8 = 0x64 + VK_F9 = 0x65 + VK_F11 = 0x67 + VK_F13 = 0x69 + VK_F16 = 0x6A + VK_F14 = 0x6B + VK_F10 = 0x6D + VK_F12 = 0x6F + VK_F15 = 0x71 + VK_HELP = 0x72 + VK_HOME = 0x73 + VK_PAGEUP = 0x74 + VK_ForwardDelete = 0x75 + VK_F4 = 0x76 + VK_END = 0x77 + VK_F2 = 0x78 + VK_PAGEDOWN = 0x79 + VK_F1 = 0x7A + VK_LEFT = 0x7B + VK_RIGHT = 0x7C + VK_DOWN = 0x7D + VK_UP = 0x7E +) diff --git a/vendor/github.com/micmonay/keybd_event/keybd_event.go b/vendor/github.com/micmonay/keybd_event/keybd_event.go new file mode 100644 index 00000000..a4bd58b3 --- /dev/null +++ b/vendor/github.com/micmonay/keybd_event/keybd_event.go @@ -0,0 +1,86 @@ +// Package keybd_event is used for a key press simulated in Windows, Linux and Mac +package keybd_event + +//KeyBonding type for keybd_event +type KeyBonding struct { + hasCTRL bool + hasALT bool + hasSHIFT bool + hasRCTRL bool + hasRSHIFT bool + hasALTGR bool + hasSuper bool + keys []int +} + +//NewKeyBonding Use for create struct KeyBounding +func NewKeyBonding() (KeyBonding, error) { + keyBounding := KeyBonding{} + keyBounding.Clear() + err := initKeyBD() + if err != nil { + return keyBounding, err + } + return keyBounding, nil +} + +//Clear clean instance +func (k *KeyBonding) Clear() { + k.hasALT = false + k.hasCTRL = false + k.hasSHIFT = false + k.hasRCTRL = false + k.hasRSHIFT = false + k.hasALTGR = false + k.hasSuper = false + k.keys = []int{} +} + +//SetKeys set keys +func (k *KeyBonding) SetKeys(keys ...int) { + k.keys = keys +} + +//AddKey add one key pressed +func (k *KeyBonding) AddKey(key int) { + k.keys = append(k.keys, key) +} + +//HasALT If key ALT pressed +func (k *KeyBonding) HasALT(b bool) { + k.hasALT = b +} + +//HasCTRL If key CTRL pressed +func (k *KeyBonding) HasCTRL(b bool) { + k.hasCTRL = b +} + +//HasSHIFT If key SHIFT pressed +func (k *KeyBonding) HasSHIFT(b bool) { + k.hasSHIFT = b +} + +//HasALTGR If key ALTGR pressed +func (k *KeyBonding) HasALTGR(b bool) { + k.hasALTGR = b +} + +//HasSuper If key Super pressed +func (k *KeyBonding) HasSuper(b bool) { + k.hasSuper = b +} + +//HasCTRLR If key CTRLR pressed +// +//This is currently not supported on macOS +func (k *KeyBonding) HasCTRLR(b bool) { + k.hasRCTRL = b +} + +//HasSHIFTR If key SHIFTR pressed +// +//This is currently not supported on macOS +func (k *KeyBonding) HasSHIFTR(b bool) { + k.hasRSHIFT = b +} diff --git a/vendor/github.com/micmonay/keybd_event/keybd_linux.go b/vendor/github.com/micmonay/keybd_event/keybd_linux.go new file mode 100644 index 00000000..5ef5a130 --- /dev/null +++ b/vendor/github.com/micmonay/keybd_event/keybd_linux.go @@ -0,0 +1,549 @@ +package keybd_event + +/* + #include +*/ +import "C" +import ( + "encoding/binary" + "errors" + "os" + "syscall" +) + +type uinput_user_dev C.struct_uinput_user_dev +type timeval C.struct_timeval +type input_event C.struct_input_event + +var fd *os.File +var X11 = true + +const ( + _EV_KEY = C.EV_KEY + _EV_SYN = C.EV_SYN + _UI_SET_EVBIT = C.UI_SET_EVBIT + _UI_SET_KEYBIT = C.UI_SET_KEYBIT + _UI_DEV_CREATE = C.UI_DEV_CREATE + _UI_DEV_DESTROY = C.UI_DEV_DESTROY + _UINPUT_MAX_NAME_SIZE = C.UINPUT_MAX_NAME_SIZE + _VK_LEFTCTRL = 29 + _VK_RIGHTCTRL = 97 + _VK_CTRL = 29 + _VK_LEFTSHIFT = 42 + _VK_RIGHTSHIFT = 54 + _VK_SHIFT = 42 + _VK_LEFTALT = 56 + _VK_RIGHTALT = 100 + _VK_ALT = 56 + _VK_LEFTMETA = 125 + _VK_RIGHTMETA = 126 +) + +func initKeyBD() error { + if fd != nil { + return nil + } + path, err := getFileUInput() + if err != nil { + return err + } + fdInit, err := os.OpenFile(path, os.O_WRONLY|syscall.O_NONBLOCK, os.ModeDevice) + fd = fdInit + if err != nil { + if os.IsPermission(err) { + return errors.New("permission error for " + path + " try cmd : sudo chmod +0666 " + path) + } else { + return err + } + } + _, _, errCall := syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), _UI_SET_EVBIT, uintptr(_EV_KEY)) + if errCall != 0 { + return err + } + _, _, errCall = syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), _UI_SET_EVBIT, uintptr(_EV_SYN)) + if errCall != 0 { + return err + } + keyEventSet() + uidev := uinput_user_dev{} + for i, c := range "keybd interface" { + uidev.name[i] = C.char(c) + } + uidev.id.bustype = C.BUS_USB + uidev.id.vendor = 0x1 + uidev.id.product = 0x1 + uidev.id.version = 0x1 + err = binary.Write(fd, binary.LittleEndian, &uidev) + if err != nil { + return err + } + //Create device + _, _, errCall = syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), _UI_DEV_CREATE, 0) + if errCall != 0 { + return err + } + return nil +} + +//actualy don't use +func destroyLinuxUInput() { + syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), _UI_DEV_DESTROY, 0) + fd.Close() +} +func getFileUInput() (string, error) { + if _, err := os.Stat("/dev/uinput"); err == nil { + return "/dev/uinput", nil + } + if _, err := os.Stat("/dev/input/uinput"); err == nil { + return "/dev/input/uinput", nil + } + err := errors.New("Not found uinput file. Try this cmd 'sudo modprobe uinput'") + return "", err +} + +// Press key(s) +func (k *KeyBonding) Press() error { + var err error + if k.hasALT { + err = downKey(_VK_ALT) + if err != nil { + return err + } + } + if k.hasSHIFT { + err = downKey(_VK_SHIFT) + if err != nil { + return err + } + } + if k.hasCTRL { + err = downKey(_VK_CTRL) + if err != nil { + return err + } + } + if k.hasRSHIFT { + err = downKey(_VK_RIGHTSHIFT) + if err != nil { + return err + } + } + if k.hasRCTRL { + err = downKey(_VK_RIGHTCTRL) + if err != nil { + return err + } + } + if k.hasALTGR { + err = downKey(_VK_RIGHTALT) + if err != nil { + return err + } + } + if k.hasSuper { + err = downKey(_VK_LEFTMETA) + if err != nil { + return err + } + } + for _, key := range k.keys { + err = downKey(key) + if err != nil { + return err + } + } + err = sync() + if err != nil { + return err + } + return nil +} + +// Release key(s) +func (k *KeyBonding) Release() error { + var err error + if k.hasALT { + err = upKey(_VK_ALT) + if err != nil { + return err + } + } + if k.hasSHIFT { + err = upKey(_VK_SHIFT) + if err != nil { + return err + } + } + if k.hasCTRL { + err = upKey(_VK_CTRL) + if err != nil { + return err + } + } + if k.hasRSHIFT { + err = upKey(_VK_RIGHTSHIFT) + if err != nil { + return err + } + } + if k.hasRCTRL { + err = upKey(_VK_RIGHTCTRL) + if err != nil { + return err + } + } + if k.hasALTGR { + err = upKey(_VK_RIGHTALT) + if err != nil { + return err + } + } + if k.hasSuper { + err := upKey(_VK_LEFTMETA) + if err != nil { + return err + } + } + for _, key := range k.keys { + err = upKey(key) + if err != nil { + return err + } + } + err = sync() + if err != nil { + return err + } + //Destroy device + + return nil +} + +// Launch key bounding +func (k *KeyBonding) Launching() error { + k.Press() + //key up + k.Release() + return nil +} +func keyEventSet() error { + for i := 0; i < 256; i++ { + _, _, errCall := syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), _UI_SET_KEYBIT, uintptr(i)) + if errCall != 0 { + return errCall + } + } + return nil +} +func downKey(key int) error { + ev := input_event{} + ev._type = _EV_KEY + ev.code = C.__u16(key) + ev.value = 1 + err := binary.Write(fd, binary.LittleEndian, &ev) + if err != nil { + return err + } + return nil +} +func sync() error { + ev := input_event{} + ev._type = _EV_SYN + ev.code = 0 + ev.value = 0 + err := binary.Write(fd, binary.LittleEndian, &ev) + if err != nil { + return err + } + return nil +} +func upKey(key int) error { + ev := input_event{} + ev._type = _EV_KEY + ev.code = C.__u16(key) + ev.value = 0 + err := binary.Write(fd, binary.LittleEndian, &ev) + if err != nil { + return err + } + return nil +} + +const ( + VK_SP1 = 41 + VK_SP2 = 12 + VK_SP3 = 13 + VK_SP4 = 26 + VK_SP5 = 27 + VK_SP6 = 39 + VK_SP7 = 40 + VK_SP8 = 43 + VK_SP9 = 51 + VK_SP10 = 52 + VK_SP11 = 53 + VK_SP12 = 86 + + VK_UP = 103 + VK_DOWN = 108 + VK_LEFT = 105 + VK_RIGHT = 106 + + VK_ESC = 1 + VK_1 = 2 + VK_2 = 3 + VK_3 = 4 + VK_4 = 5 + VK_5 = 6 + VK_6 = 7 + VK_7 = 8 + VK_8 = 9 + VK_9 = 10 + VK_0 = 11 + VK_Q = 16 + VK_W = 17 + VK_E = 18 + VK_R = 19 + VK_T = 20 + VK_Y = 21 + VK_U = 22 + VK_I = 23 + VK_O = 24 + VK_P = 25 + VK_A = 30 + VK_S = 31 + VK_D = 32 + VK_F = 33 + VK_G = 34 + VK_H = 35 + VK_J = 36 + VK_K = 37 + VK_L = 38 + VK_Z = 44 + VK_X = 45 + VK_C = 46 + VK_V = 47 + VK_B = 48 + VK_N = 49 + VK_M = 50 + VK_F1 = 59 + VK_F2 = 60 + VK_F3 = 61 + VK_F4 = 62 + VK_F5 = 63 + VK_F6 = 64 + VK_F7 = 65 + VK_F8 = 66 + VK_F9 = 67 + VK_F10 = 68 + VK_F11 = 87 + VK_F12 = 88 + + VK_NUMLOCK = 69 + VK_SCROLLLOCK = 70 + VK_RESERVED = 0 + VK_MINUS = 12 + VK_EQUAL = 13 + VK_BACKSPACE = 14 + VK_TAB = 15 + VK_LEFTBRACE = 26 + VK_RIGHTBRACE = 27 + VK_ENTER = 28 + VK_SEMICOLON = 39 + VK_APOSTROPHE = 40 + VK_GRAVE = 41 + VK_BACKSLASH = 43 + VK_COMMA = 51 + VK_DOT = 52 + VK_SLASH = 53 + VK_SPACE = 57 + VK_CAPSLOCK = 58 + + VK_KP0 = 82 + VK_KP1 = 79 + VK_KP2 = 80 + VK_KP3 = 81 + VK_KP4 = 75 + VK_KP5 = 76 + VK_KP6 = 77 + VK_KP7 = 71 + VK_KP8 = 72 + VK_KP9 = 73 + VK_KPMINUS = 74 + VK_KPPLUS = 78 + VK_KPDOT = 83 + VK_KPJPCOMMA = 95 + VK_KPENTER = 96 + VK_KPSLASH = 98 + VK_KPASTERISK = 55 + VK_KPEQUAL = 117 + VK_KPPLUSMINUS = 118 + VK_KPCOMMA = 121 + + VK_ZENKAKUHANKAKU = 85 + VK_102ND = 86 + VK_RO = 89 + VK_KATAKANA = 90 + VK_HIRAGANA = 91 + VK_HENKAN = 92 + VK_KATAKANAHIRAGANA = 93 + VK_MUHENKAN = 94 + VK_SYSRQ = 99 + VK_LINEFEED = 101 + VK_HOME = 102 + VK_PAGEUP = 104 + VK_END = 107 + VK_PAGEDOWN = 109 + VK_INSERT = 110 + VK_DELETE = 111 + VK_MACRO = 112 + VK_MUTE = 113 + VK_VOLUMEDOWN = 114 + VK_VOLUMEUP = 115 + VK_POWER = 116 /* SC System Power Down */ + VK_PAUSE = 119 + VK_SCALE = 120 /* AL Compiz Scale (Expose) */ + + VK_HANGEUL = 122 + VK_HANGUEL = VK_HANGEUL + VK_HANJA = 123 + VK_YEN = 124 + VK_LEFTMETA = 125 + VK_RIGHTMETA = 126 + VK_COMPOSE = 127 + + VK_STOP = 128 /* AC Stop */ + VK_AGAIN = 129 + VK_PROPS = 130 /* AC Properties */ + VK_UNDO = 131 /* AC Undo */ + VK_FRONT = 132 + VK_COPY = 133 /* AC Copy */ + VK_OPEN = 134 /* AC Open */ + VK_PASTE = 135 /* AC Paste */ + VK_FIND = 136 /* AC Search */ + VK_CUT = 137 /* AC Cut */ + VK_HELP = 138 /* AL Integrated Help Center */ + VK_MENU = 139 /* Menu (show menu) */ + VK_CALC = 140 /* AL Calculator */ + VK_SETUP = 141 + VK_SLEEP = 142 /* SC System Sleep */ + VK_WAKEUP = 143 /* System Wake Up */ + VK_FILE = 144 /* AL Local Machine Browser */ + VK_SENDFILE = 145 + VK_DELETEFILE = 146 + VK_XFER = 147 + VK_PROG1 = 148 + VK_PROG2 = 149 + VK_WWW = 150 /* AL Internet Browser */ + VK_MSDOS = 151 + VK_COFFEE = 152 /* AL Terminal Lock/Screensaver */ + VK_SCREENLOCK = VK_COFFEE + VK_ROTATE_DISPLAY = 153 /* Display orientation for e.g. tablets */ + VK_DIRECTION = VK_ROTATE_DISPLAY + VK_CYCLEWINDOWS = 154 + VK_MAIL = 155 + VK_BOOKMARKS = 156 /* AC Bookmarks */ + VK_COMPUTER = 157 + VK_BACK = 158 /* AC Back */ + VK_FORWARD = 159 /* AC Forward */ + VK_CLOSECD = 160 + VK_EJECTCD = 161 + VK_EJECTCLOSECD = 162 + VK_NEXTSONG = 163 + VK_PLAYPAUSE = 164 + VK_PREVIOUSSONG = 165 + VK_STOPCD = 166 + VK_RECORD = 167 + VK_REWIND = 168 + VK_PHONE = 169 /* Media Select Telephone */ + VK_ISO = 170 + VK_CONFIG = 171 /* AL Consumer Control Configuration */ + VK_HOMEPAGE = 172 /* AC Home */ + VK_REFRESH = 173 /* AC Refresh */ + VK_EXIT = 174 /* AC Exit */ + VK_MOVE = 175 + VK_EDIT = 176 + VK_SCROLLUP = 177 + VK_SCROLLDOWN = 178 + VK_KPLEFTPAREN = 179 + VK_KPRIGHTPAREN = 180 + VK_NEW = 181 /* AC New */ + VK_REDO = 182 /* AC Redo/Repeat */ + + VK_F13 = 183 + VK_F14 = 184 + VK_F15 = 185 + VK_F16 = 186 + VK_F17 = 187 + VK_F18 = 188 + VK_F19 = 189 + VK_F20 = 190 + VK_F21 = 191 + VK_F22 = 192 + VK_F23 = 193 + VK_F24 = 194 + + VK_PLAYCD = 200 + VK_PAUSECD = 201 + VK_PROG3 = 202 + VK_PROG4 = 203 + VK_DASHBOARD = 204 /* AL Dashboard */ + VK_SUSPEND = 205 + VK_CLOSE = 206 /* AC Close */ + VK_PLAY = 207 + VK_FASTFORWARD = 208 + VK_BASSBOOST = 209 + VK_PRINT = 210 /* AC Print */ + VK_HP = 211 + VK_CAMERA = 212 + VK_SOUND = 213 + VK_QUESTION = 214 + VK_EMAIL = 215 + VK_CHAT = 216 + VK_SEARCH = 217 + VK_CONNECT = 218 + VK_FINANCE = 219 /* AL Checkbook/Finance */ + VK_SPORT = 220 + VK_SHOP = 221 + VK_ALTERASE = 222 + VK_CANCEL = 223 /* AC Cancel */ + VK_BRIGHTNESSDOWN = 224 + VK_BRIGHTNESSUP = 225 + VK_MEDIA = 226 + + VK_SWITCHVIDEOMODE = 227 /* Cycle between available video + outputs (Monitor/LCD/TV-out/etc) */ + VK_KBDILLUMTOGGLE = 228 + VK_KBDILLUMDOWN = 229 + VK_KBDILLUMUP = 230 + + VK_SEND = 231 /* AC Send */ + VK_REPLY = 232 /* AC Reply */ + VK_FORWARDMAIL = 233 /* AC Forward Msg */ + VK_SAVE = 234 /* AC Save */ + VK_DOCUMENTS = 235 + + VK_BATTERY = 236 + + VK_BLUETOOTH = 237 + VK_WLAN = 238 + VK_UWB = 239 + + VK_UNKNOWN = 240 + + VK_VIDEO_NEXT = 241 /* drive next video source */ + VK_VIDEO_PREV = 242 /* drive previous video source */ + VK_BRIGHTNESS_CYCLE = 243 /* brightness up, after max is min */ + VK_BRIGHTNESS_AUTO = 244 /* Set Auto Brightness: manual + brightness control is off, + rely on ambient */ + VK_BRIGHTNESS_ZERO = VK_BRIGHTNESS_AUTO + VK_DISPLAY_OFF = 245 /* display device to off state */ + + VK_WWAN = 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ + VK_WIMAX = VK_WWAN + VK_RFKILL = 247 /* Key that controls all radios */ + + VK_MICMUTE = 248 /* Mute / unmute the microphone */ +) + +//http://thiemonge.org/getting-started-with-uinput diff --git a/vendor/github.com/micmonay/keybd_event/keybd_windows.go b/vendor/github.com/micmonay/keybd_event/keybd_windows.go new file mode 100644 index 00000000..7866a009 --- /dev/null +++ b/vendor/github.com/micmonay/keybd_event/keybd_windows.go @@ -0,0 +1,310 @@ +package keybd_event + +import ( + "syscall" +) + +var dll = syscall.NewLazyDLL("user32.dll") +var procKeyBd = dll.NewProc("keybd_event") + +// Press key(s) +func (k *KeyBonding) Press() error { + if k.hasALT { + downKey(_VK_ALT) + } + if k.hasALTGR { + downKey(_VK_ALT) + downKey(_VK_CTRL) + } + if k.hasSHIFT { + downKey(_VK_SHIFT) + } + if k.hasCTRL { + downKey(_VK_CTRL) + } + if k.hasRSHIFT { + downKey(_VK_RSHIFT) + } + if k.hasRCTRL { + downKey(_VK_RCONTROL) + } + if k.hasSuper { + downKey(_VK_LWIN) + } + for _, key := range k.keys { + downKey(key) + } + return nil +} + +//Release key(s) +func (k *KeyBonding) Release() error { + if k.hasALT { + upKey(_VK_ALT) + } + if k.hasALTGR { + upKey(_VK_ALT) + upKey(_VK_CTRL) + } + if k.hasSHIFT { + upKey(_VK_SHIFT) + } + if k.hasCTRL { + upKey(_VK_CTRL) + } + if k.hasRSHIFT { + upKey(_VK_RSHIFT) + } + if k.hasRCTRL { + upKey(_VK_RCONTROL) + } + for _, key := range k.keys { + upKey(key) + } + if k.hasSuper { + upKey(_VK_LWIN) + } + return nil +} + +// Launch key bounding +func (k *KeyBonding) Launching() error { + err := k.Press() + if err != nil { + return err + } + err = k.Release() + return err +} +func downKey(key int) { + flag := 0 + if key < 0xFFF { // Detect if the key code is virtual or no + flag |= _KEYEVENTF_SCANCODE + } else { + key -= 0xFFF + } + vkey := key + 0x80 + procKeyBd.Call(uintptr(key), uintptr(vkey), uintptr(flag), 0) +} +func upKey(key int) { + flag := _KEYEVENTF_KEYUP + if key < 0xFFF { + flag |= _KEYEVENTF_SCANCODE + } else { + key -= 0xFFF + } + vkey := key + 0x80 + procKeyBd.Call(uintptr(key), uintptr(vkey), uintptr(flag), 0) +} +func initKeyBD() error { return nil } + +const ( + // I add 0xFFF for all Virtual key + _VK_SHIFT = 0x10 + 0xFFF + _VK_CTRL = 0x11 + 0xFFF + _VK_ALT = 0x12 + 0xFFF + _VK_LSHIFT = 0xA0 + 0xFFF + _VK_RSHIFT = 0xA1 + 0xFFF + _VK_LCONTROL = 0xA2 + 0xFFF + _VK_RCONTROL = 0xA3 + 0xFFF + _VK_LWIN = 0x5B + 0xFFF + _VK_RWIN = 0x5C + 0xFFF + _KEYEVENTF_KEYUP = 0x0002 + _KEYEVENTF_SCANCODE = 0x0008 +) +const ( + VK_SP1 = 41 + VK_SP2 = 12 + VK_SP3 = 13 + VK_SP4 = 26 + VK_SP5 = 27 + VK_SP6 = 39 + VK_SP7 = 40 + VK_SP8 = 43 + VK_SP9 = 51 + VK_SP10 = 52 + VK_SP11 = 53 + VK_SP12 = 86 + + VK_ESC = 1 + VK_1 = 2 + VK_2 = 3 + VK_3 = 4 + VK_4 = 5 + VK_5 = 6 + VK_6 = 7 + VK_7 = 8 + VK_8 = 9 + VK_9 = 10 + VK_0 = 11 + VK_Q = 16 + VK_W = 17 + VK_E = 18 + VK_R = 19 + VK_T = 20 + VK_Y = 21 + VK_U = 22 + VK_I = 23 + VK_O = 24 + VK_P = 25 + VK_A = 30 + VK_S = 31 + VK_D = 32 + VK_F = 33 + VK_G = 34 + VK_H = 35 + VK_J = 36 + VK_K = 37 + VK_L = 38 + VK_Z = 44 + VK_X = 45 + VK_C = 46 + VK_V = 47 + VK_B = 48 + VK_N = 49 + VK_M = 50 + VK_F1 = 59 + VK_F2 = 60 + VK_F3 = 61 + VK_F4 = 62 + VK_F5 = 63 + VK_F6 = 64 + VK_F7 = 65 + VK_F8 = 66 + VK_F9 = 67 + VK_F10 = 68 + VK_F11 = 87 + VK_F12 = 88 + + VK_F13 = 0x7C + 0xFFF + VK_F14 = 0x7D + 0xFFF + VK_F15 = 0x7E + 0xFFF + VK_F16 = 0x7F + 0xFFF + VK_F17 = 0x80 + 0xFFF + VK_F18 = 0x81 + 0xFFF + VK_F19 = 0x82 + 0xFFF + VK_F20 = 0x83 + 0xFFF + VK_F21 = 0x84 + 0xFFF + VK_F22 = 0x85 + 0xFFF + VK_F23 = 0x86 + 0xFFF + VK_F24 = 0x87 + 0xFFF + + VK_NUMLOCK = 69 + VK_SCROLLLOCK = 70 + VK_RESERVED = 0 + VK_MINUS = 12 + VK_EQUAL = 13 + VK_BACKSPACE = 14 + VK_TAB = 15 + VK_LEFTBRACE = 26 + VK_RIGHTBRACE = 27 + VK_ENTER = 28 + VK_SEMICOLON = 39 + VK_APOSTROPHE = 40 + VK_GRAVE = 41 + VK_BACKSLASH = 43 + VK_COMMA = 51 + VK_DOT = 52 + VK_SLASH = 53 + VK_KPASTERISK = 55 + VK_SPACE = 57 + VK_CAPSLOCK = 58 + + VK_KP0 = 82 + VK_KP1 = 79 + VK_KP2 = 80 + VK_KP3 = 81 + VK_KP4 = 75 + VK_KP5 = 76 + VK_KP6 = 77 + VK_KP7 = 71 + VK_KP8 = 72 + VK_KP9 = 73 + VK_KPMINUS = 74 + VK_KPPLUS = 78 + VK_KPDOT = 83 + + // I add 0xFFF for all Virtual key + VK_LBUTTON = 0x01 + 0xFFF + VK_RBUTTON = 0x02 + 0xFFF + VK_CANCEL = 0x03 + 0xFFF + VK_MBUTTON = 0x04 + 0xFFF + VK_XBUTTON1 = 0x05 + 0xFFF + VK_XBUTTON2 = 0x06 + 0xFFF + VK_BACK = 0x08 + 0xFFF + VK_CLEAR = 0x0C + 0xFFF + VK_PAUSE = 0x13 + 0xFFF + VK_CAPITAL = 0x14 + 0xFFF + VK_KANA = 0x15 + 0xFFF + VK_HANGUEL = 0x15 + 0xFFF + VK_HANGUL = 0x15 + 0xFFF + VK_JUNJA = 0x17 + 0xFFF + VK_FINAL = 0x18 + 0xFFF + VK_HANJA = 0x19 + 0xFFF + VK_KANJI = 0x19 + 0xFFF + VK_CONVERT = 0x1C + 0xFFF + VK_NONCONVERT = 0x1D + 0xFFF + VK_ACCEPT = 0x1E + 0xFFF + VK_MODECHANGE = 0x1F + 0xFFF + VK_PAGEUP = 0x21 + 0xFFF + VK_PAGEDOWN = 0x22 + 0xFFF + VK_END = 0x23 + 0xFFF + VK_HOME = 0x24 + 0xFFF + VK_LEFT = 0x25 + 0xFFF + VK_UP = 0x26 + 0xFFF + VK_RIGHT = 0x27 + 0xFFF + VK_DOWN = 0x28 + 0xFFF + VK_SELECT = 0x29 + 0xFFF + VK_PRINT = 0x2A + 0xFFF + VK_EXECUTE = 0x2B + 0xFFF + VK_SNAPSHOT = 0x2C + 0xFFF + VK_INSERT = 0x2D + 0xFFF + VK_DELETE = 0x2E + 0xFFF + VK_HELP = 0x2F + 0xFFF + + VK_SCROLL = 0x91 + 0xFFF + VK_LMENU = 0xA4 + 0xFFF + VK_RMENU = 0xA5 + 0xFFF + VK_BROWSER_BACK = 0xA6 + 0xFFF + VK_BROWSER_FORWARD = 0xA7 + 0xFFF + VK_BROWSER_REFRESH = 0xA8 + 0xFFF + VK_BROWSER_STOP = 0xA9 + 0xFFF + VK_BROWSER_SEARCH = 0xAA + 0xFFF + VK_BROWSER_FAVORITES = 0xAB + 0xFFF + VK_BROWSER_HOME = 0xAC + 0xFFF + VK_VOLUME_MUTE = 0xAD + 0xFFF + VK_VOLUME_DOWN = 0xAE + 0xFFF + VK_VOLUME_UP = 0xAF + 0xFFF + VK_MEDIA_NEXT_TRACK = 0xB0 + 0xFFF + VK_MEDIA_PREV_TRACK = 0xB1 + 0xFFF + VK_MEDIA_STOP = 0xB2 + 0xFFF + VK_MEDIA_PLAY_PAUSE = 0xB3 + 0xFFF + VK_LAUNCH_MAIL = 0xB4 + 0xFFF + VK_LAUNCH_MEDIA_SELECT = 0xB5 + 0xFFF + VK_LAUNCH_APP1 = 0xB6 + 0xFFF + VK_LAUNCH_APP2 = 0xB7 + 0xFFF + VK_OEM_1 = 0xBA + 0xFFF + VK_OEM_PLUS = 0xBB + 0xFFF + VK_OEM_COMMA = 0xBC + 0xFFF + VK_OEM_MINUS = 0xBD + 0xFFF + VK_OEM_PERIOD = 0xBE + 0xFFF + VK_OEM_2 = 0xBF + 0xFFF + VK_OEM_3 = 0xC0 + 0xFFF + VK_OEM_4 = 0xDB + 0xFFF + VK_OEM_5 = 0xDC + 0xFFF + VK_OEM_6 = 0xDD + 0xFFF + VK_OEM_7 = 0xDE + 0xFFF + VK_OEM_8 = 0xDF + 0xFFF + VK_OEM_102 = 0xE2 + 0xFFF + VK_PROCESSKEY = 0xE5 + 0xFFF + VK_PACKET = 0xE7 + 0xFFF + VK_ATTN = 0xF6 + 0xFFF + VK_CRSEL = 0xF7 + 0xFFF + VK_EXSEL = 0xF8 + 0xFFF + VK_EREOF = 0xF9 + 0xFFF + VK_PLAY = 0xFA + 0xFFF + VK_ZOOM = 0xFB + 0xFFF + VK_NONAME = 0xFC + 0xFFF + VK_PA1 = 0xFD + 0xFFF + VK_OEM_CLEAR = 0xFE + 0xFFF +) diff --git a/vendor/github.com/micmonay/keybd_event/keyboard.png b/vendor/github.com/micmonay/keybd_event/keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..e9622fc8fabd6e73497709df7884ac18abdefeb5 GIT binary patch literal 40492 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/moby/term/.gitignore b/vendor/github.com/moby/term/.gitignore new file mode 100644 index 00000000..b0747ff0 --- /dev/null +++ b/vendor/github.com/moby/term/.gitignore @@ -0,0 +1,8 @@ +# if you want to ignore files created by your editor/tools, consider using a +# global .gitignore or .git/info/exclude see https://help.github.com/articles/ignoring-files +.* +!.github +!.gitignore +profile.out +# support running go modules in vendor mode for local development +vendor/ diff --git a/vendor/github.com/moby/term/LICENSE b/vendor/github.com/moby/term/LICENSE new file mode 100644 index 00000000..6d8d58fb --- /dev/null +++ b/vendor/github.com/moby/term/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2018 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/moby/term/README.md b/vendor/github.com/moby/term/README.md new file mode 100644 index 00000000..0ce92cc3 --- /dev/null +++ b/vendor/github.com/moby/term/README.md @@ -0,0 +1,36 @@ +# term - utilities for dealing with terminals + +![Test](https://github.com/moby/term/workflows/Test/badge.svg) [![GoDoc](https://godoc.org/github.com/moby/term?status.svg)](https://godoc.org/github.com/moby/term) [![Go Report Card](https://goreportcard.com/badge/github.com/moby/term)](https://goreportcard.com/report/github.com/moby/term) + +term provides structures and helper functions to work with terminal (state, sizes). + +#### Using term + +```go +package main + +import ( + "log" + "os" + + "github.com/moby/term" +) + +func main() { + fd := os.Stdin.Fd() + if term.IsTerminal(fd) { + ws, err := term.GetWinsize(fd) + if err != nil { + log.Fatalf("term.GetWinsize: %s", err) + } + log.Printf("%d:%d\n", ws.Height, ws.Width) + } +} +``` + +## Contributing + +Want to hack on term? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply. + +## Copyright and license +Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons. diff --git a/vendor/github.com/moby/term/ascii.go b/vendor/github.com/moby/term/ascii.go new file mode 100644 index 00000000..55873c05 --- /dev/null +++ b/vendor/github.com/moby/term/ascii.go @@ -0,0 +1,66 @@ +package term + +import ( + "fmt" + "strings" +) + +// ASCII list the possible supported ASCII key sequence +var ASCII = []string{ + "ctrl-@", + "ctrl-a", + "ctrl-b", + "ctrl-c", + "ctrl-d", + "ctrl-e", + "ctrl-f", + "ctrl-g", + "ctrl-h", + "ctrl-i", + "ctrl-j", + "ctrl-k", + "ctrl-l", + "ctrl-m", + "ctrl-n", + "ctrl-o", + "ctrl-p", + "ctrl-q", + "ctrl-r", + "ctrl-s", + "ctrl-t", + "ctrl-u", + "ctrl-v", + "ctrl-w", + "ctrl-x", + "ctrl-y", + "ctrl-z", + "ctrl-[", + "ctrl-\\", + "ctrl-]", + "ctrl-^", + "ctrl-_", +} + +// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code. +func ToBytes(keys string) ([]byte, error) { + codes := []byte{} +next: + for _, key := range strings.Split(keys, ",") { + if len(key) != 1 { + for code, ctrl := range ASCII { + if ctrl == key { + codes = append(codes, byte(code)) + continue next + } + } + if key == "DEL" { + codes = append(codes, 127) + } else { + return nil, fmt.Errorf("Unknown character: '%s'", key) + } + } else { + codes = append(codes, key[0]) + } + } + return codes, nil +} diff --git a/vendor/github.com/moby/term/proxy.go b/vendor/github.com/moby/term/proxy.go new file mode 100644 index 00000000..c47756b8 --- /dev/null +++ b/vendor/github.com/moby/term/proxy.go @@ -0,0 +1,88 @@ +package term + +import ( + "io" +) + +// EscapeError is special error which returned by a TTY proxy reader's Read() +// method in case its detach escape sequence is read. +type EscapeError struct{} + +func (EscapeError) Error() string { + return "read escape sequence" +} + +// escapeProxy is used only for attaches with a TTY. It is used to proxy +// stdin keypresses from the underlying reader and look for the passed in +// escape key sequence to signal a detach. +type escapeProxy struct { + escapeKeys []byte + escapeKeyPos int + r io.Reader + buf []byte +} + +// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader +// and detects when the specified escape keys are read, in which case the Read +// method will return an error of type EscapeError. +func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader { + return &escapeProxy{ + escapeKeys: escapeKeys, + r: r, + } +} + +func (r *escapeProxy) Read(buf []byte) (n int, err error) { + if len(r.escapeKeys) > 0 && r.escapeKeyPos == len(r.escapeKeys) { + return 0, EscapeError{} + } + + if len(r.buf) > 0 { + n = copy(buf, r.buf) + r.buf = r.buf[n:] + } + + nr, err := r.r.Read(buf[n:]) + n += nr + if len(r.escapeKeys) == 0 { + return n, err + } + + for i := 0; i < n; i++ { + if buf[i] == r.escapeKeys[r.escapeKeyPos] { + r.escapeKeyPos++ + + // Check if the full escape sequence is matched. + if r.escapeKeyPos == len(r.escapeKeys) { + n = i + 1 - r.escapeKeyPos + if n < 0 { + n = 0 + } + return n, EscapeError{} + } + continue + } + + // If we need to prepend a partial escape sequence from the previous + // read, make sure the new buffer size doesn't exceed len(buf). + // Otherwise, preserve any extra data in a buffer for the next read. + if i < r.escapeKeyPos { + preserve := make([]byte, 0, r.escapeKeyPos+n) + preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...) + preserve = append(preserve, buf[:n]...) + n = copy(buf, preserve) + i += r.escapeKeyPos + r.buf = append(r.buf, preserve[n:]...) + } + r.escapeKeyPos = 0 + } + + // If we're in the middle of reading an escape sequence, make sure we don't + // let the caller read it. If later on we find that this is not the escape + // sequence, we'll prepend it back to buf. + n -= r.escapeKeyPos + if n < 0 { + n = 0 + } + return n, err +} diff --git a/vendor/github.com/moby/term/tc.go b/vendor/github.com/moby/term/tc.go new file mode 100644 index 00000000..65556027 --- /dev/null +++ b/vendor/github.com/moby/term/tc.go @@ -0,0 +1,19 @@ +// +build !windows + +package term + +import ( + "golang.org/x/sys/unix" +) + +func tcget(fd uintptr) (*Termios, error) { + p, err := unix.IoctlGetTermios(int(fd), getTermios) + if err != nil { + return nil, err + } + return p, nil +} + +func tcset(fd uintptr, p *Termios) error { + return unix.IoctlSetTermios(int(fd), setTermios, p) +} diff --git a/vendor/github.com/moby/term/term.go b/vendor/github.com/moby/term/term.go new file mode 100644 index 00000000..29c6acf1 --- /dev/null +++ b/vendor/github.com/moby/term/term.go @@ -0,0 +1,120 @@ +// +build !windows + +// Package term provides structures and helper functions to work with +// terminal (state, sizes). +package term + +import ( + "errors" + "fmt" + "io" + "os" + "os/signal" + + "golang.org/x/sys/unix" +) + +var ( + // ErrInvalidState is returned if the state of the terminal is invalid. + ErrInvalidState = errors.New("Invalid terminal state") +) + +// State represents the state of the terminal. +type State struct { + termios Termios +} + +// Winsize represents the size of the terminal window. +type Winsize struct { + Height uint16 + Width uint16 + x uint16 + y uint16 +} + +// StdStreams returns the standard streams (stdin, stdout, stderr). +func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { + return os.Stdin, os.Stdout, os.Stderr +} + +// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. +func GetFdInfo(in interface{}) (uintptr, bool) { + var inFd uintptr + var isTerminalIn bool + if file, ok := in.(*os.File); ok { + inFd = file.Fd() + isTerminalIn = IsTerminal(inFd) + } + return inFd, isTerminalIn +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + _, err := tcget(fd) + return err == nil +} + +// RestoreTerminal restores the terminal connected to the given file descriptor +// to a previous state. +func RestoreTerminal(fd uintptr, state *State) error { + if state == nil { + return ErrInvalidState + } + return tcset(fd, &state.termios) +} + +// SaveState saves the state of the terminal connected to the given file descriptor. +func SaveState(fd uintptr) (*State, error) { + termios, err := tcget(fd) + if err != nil { + return nil, err + } + return &State{termios: *termios}, nil +} + +// DisableEcho applies the specified state to the terminal connected to the file +// descriptor, with echo disabled. +func DisableEcho(fd uintptr, state *State) error { + newState := state.termios + newState.Lflag &^= unix.ECHO + + if err := tcset(fd, &newState); err != nil { + return err + } + handleInterrupt(fd, state) + return nil +} + +// SetRawTerminal puts the terminal connected to the given file descriptor into +// raw mode and returns the previous state. On UNIX, this puts both the input +// and output into raw mode. On Windows, it only puts the input into raw mode. +func SetRawTerminal(fd uintptr) (*State, error) { + oldState, err := MakeRaw(fd) + if err != nil { + return nil, err + } + handleInterrupt(fd, oldState) + return oldState, err +} + +// SetRawTerminalOutput puts the output of terminal connected to the given file +// descriptor into raw mode. On UNIX, this does nothing and returns nil for the +// state. On Windows, it disables LF -> CRLF translation. +func SetRawTerminalOutput(fd uintptr) (*State, error) { + return nil, nil +} + +func handleInterrupt(fd uintptr, state *State) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, os.Interrupt) + go func() { + for range sigchan { + // quit cleanly and the new terminal item is on a new line + fmt.Println() + signal.Stop(sigchan) + close(sigchan) + RestoreTerminal(fd, state) + os.Exit(1) + } + }() +} diff --git a/vendor/github.com/moby/term/term_windows.go b/vendor/github.com/moby/term/term_windows.go new file mode 100644 index 00000000..ba82960d --- /dev/null +++ b/vendor/github.com/moby/term/term_windows.go @@ -0,0 +1,231 @@ +package term + +import ( + "io" + "os" + "os/signal" + + windowsconsole "github.com/moby/term/windows" + "golang.org/x/sys/windows" +) + +// State holds the console mode for the terminal. +type State struct { + mode uint32 +} + +// Winsize is used for window size. +type Winsize struct { + Height uint16 + Width uint16 +} + +// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console +var vtInputSupported bool + +// StdStreams returns the standard streams (stdin, stdout, stderr). +func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { + // Turn on VT handling on all std handles, if possible. This might + // fail, in which case we will fall back to terminal emulation. + var ( + emulateStdin, emulateStdout, emulateStderr bool + + mode uint32 + ) + + fd := windows.Handle(os.Stdin.Fd()) + if err := windows.GetConsoleMode(fd, &mode); err == nil { + // Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. + if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil { + emulateStdin = true + } else { + vtInputSupported = true + } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + _ = windows.SetConsoleMode(fd, mode) + } + + fd = windows.Handle(os.Stdout.Fd()) + if err := windows.GetConsoleMode(fd, &mode); err == nil { + // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it. + if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil { + emulateStdout = true + } else { + _ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) + } + } + + fd = windows.Handle(os.Stderr.Fd()) + if err := windows.GetConsoleMode(fd, &mode); err == nil { + // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it. + if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil { + emulateStderr = true + } else { + _ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) + } + } + + // Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and + // STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as + // go-ansiterm hasn't switch to x/sys/windows. + // TODO: switch back to x/sys/windows once go-ansiterm has switched + if emulateStdin { + h := uint32(windows.STD_INPUT_HANDLE) + stdIn = windowsconsole.NewAnsiReader(int(h)) + } else { + stdIn = os.Stdin + } + + if emulateStdout { + h := uint32(windows.STD_OUTPUT_HANDLE) + stdOut = windowsconsole.NewAnsiWriter(int(h)) + } else { + stdOut = os.Stdout + } + + if emulateStderr { + h := uint32(windows.STD_ERROR_HANDLE) + stdErr = windowsconsole.NewAnsiWriter(int(h)) + } else { + stdErr = os.Stderr + } + + return +} + +// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. +func GetFdInfo(in interface{}) (uintptr, bool) { + return windowsconsole.GetHandleInfo(in) +} + +// GetWinsize returns the window size based on the specified file descriptor. +func GetWinsize(fd uintptr) (*Winsize, error) { + var info windows.ConsoleScreenBufferInfo + if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { + return nil, err + } + + winsize := &Winsize{ + Width: uint16(info.Window.Right - info.Window.Left + 1), + Height: uint16(info.Window.Bottom - info.Window.Top + 1), + } + + return winsize, nil +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + var mode uint32 + err := windows.GetConsoleMode(windows.Handle(fd), &mode) + return err == nil +} + +// RestoreTerminal restores the terminal connected to the given file descriptor +// to a previous state. +func RestoreTerminal(fd uintptr, state *State) error { + return windows.SetConsoleMode(windows.Handle(fd), state.mode) +} + +// SaveState saves the state of the terminal connected to the given file descriptor. +func SaveState(fd uintptr) (*State, error) { + var mode uint32 + + if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil { + return nil, err + } + + return &State{mode: mode}, nil +} + +// DisableEcho disables echo for the terminal connected to the given file descriptor. +// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx +func DisableEcho(fd uintptr, state *State) error { + mode := state.mode + mode &^= windows.ENABLE_ECHO_INPUT + mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT + err := windows.SetConsoleMode(windows.Handle(fd), mode) + if err != nil { + return err + } + + // Register an interrupt handler to catch and restore prior state + restoreAtInterrupt(fd, state) + return nil +} + +// SetRawTerminal puts the terminal connected to the given file descriptor into +// raw mode and returns the previous state. On UNIX, this puts both the input +// and output into raw mode. On Windows, it only puts the input into raw mode. +func SetRawTerminal(fd uintptr) (*State, error) { + state, err := MakeRaw(fd) + if err != nil { + return nil, err + } + + // Register an interrupt handler to catch and restore prior state + restoreAtInterrupt(fd, state) + return state, err +} + +// SetRawTerminalOutput puts the output of terminal connected to the given file +// descriptor into raw mode. On UNIX, this does nothing and returns nil for the +// state. On Windows, it disables LF -> CRLF translation. +func SetRawTerminalOutput(fd uintptr) (*State, error) { + state, err := SaveState(fd) + if err != nil { + return nil, err + } + + // Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this + // version of Windows. + _ = windows.SetConsoleMode(windows.Handle(fd), state.mode|windows.DISABLE_NEWLINE_AUTO_RETURN) + return state, err +} + +// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be restored. +func MakeRaw(fd uintptr) (*State, error) { + state, err := SaveState(fd) + if err != nil { + return nil, err + } + + mode := state.mode + + // See + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx + + // Disable these modes + mode &^= windows.ENABLE_ECHO_INPUT + mode &^= windows.ENABLE_LINE_INPUT + mode &^= windows.ENABLE_MOUSE_INPUT + mode &^= windows.ENABLE_WINDOW_INPUT + mode &^= windows.ENABLE_PROCESSED_INPUT + + // Enable these modes + mode |= windows.ENABLE_EXTENDED_FLAGS + mode |= windows.ENABLE_INSERT_MODE + mode |= windows.ENABLE_QUICK_EDIT_MODE + if vtInputSupported { + mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT + } + + err = windows.SetConsoleMode(windows.Handle(fd), mode) + if err != nil { + return nil, err + } + return state, nil +} + +func restoreAtInterrupt(fd uintptr, state *State) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, os.Interrupt) + + go func() { + _ = <-sigchan + _ = RestoreTerminal(fd, state) + os.Exit(0) + }() +} diff --git a/vendor/github.com/moby/term/termios.go b/vendor/github.com/moby/term/termios.go new file mode 100644 index 00000000..0f028e22 --- /dev/null +++ b/vendor/github.com/moby/term/termios.go @@ -0,0 +1,35 @@ +// +build !windows + +package term + +import ( + "golang.org/x/sys/unix" +) + +// Termios is the Unix API for terminal I/O. +type Termios = unix.Termios + +// MakeRaw puts the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + termios, err := tcget(fd) + if err != nil { + return nil, err + } + + oldState := State{termios: *termios} + + termios.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) + termios.Oflag &^= unix.OPOST + termios.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) + termios.Cflag &^= (unix.CSIZE | unix.PARENB) + termios.Cflag |= unix.CS8 + termios.Cc[unix.VMIN] = 1 + termios.Cc[unix.VTIME] = 0 + + if err := tcset(fd, termios); err != nil { + return nil, err + } + return &oldState, nil +} diff --git a/vendor/github.com/moby/term/termios_bsd.go b/vendor/github.com/moby/term/termios_bsd.go new file mode 100644 index 00000000..922dd4ba --- /dev/null +++ b/vendor/github.com/moby/term/termios_bsd.go @@ -0,0 +1,12 @@ +// +build darwin freebsd openbsd netbsd + +package term + +import ( + "golang.org/x/sys/unix" +) + +const ( + getTermios = unix.TIOCGETA + setTermios = unix.TIOCSETA +) diff --git a/vendor/github.com/moby/term/termios_nonbsd.go b/vendor/github.com/moby/term/termios_nonbsd.go new file mode 100644 index 00000000..038fd61b --- /dev/null +++ b/vendor/github.com/moby/term/termios_nonbsd.go @@ -0,0 +1,12 @@ +//+build !darwin,!freebsd,!netbsd,!openbsd,!windows + +package term + +import ( + "golang.org/x/sys/unix" +) + +const ( + getTermios = unix.TCGETS + setTermios = unix.TCSETS +) diff --git a/vendor/github.com/moby/term/windows/ansi_reader.go b/vendor/github.com/moby/term/windows/ansi_reader.go new file mode 100644 index 00000000..15525152 --- /dev/null +++ b/vendor/github.com/moby/term/windows/ansi_reader.go @@ -0,0 +1,252 @@ +// +build windows + +package windowsconsole + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "strings" + "unsafe" + + ansiterm "github.com/Azure/go-ansiterm" + "github.com/Azure/go-ansiterm/winterm" +) + +const ( + escapeSequence = ansiterm.KEY_ESC_CSI +) + +// ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation. +type ansiReader struct { + file *os.File + fd uintptr + buffer []byte + cbBuffer int + command []byte +} + +// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a +// Windows console input handle. +func NewAnsiReader(nFile int) io.ReadCloser { + file, fd := winterm.GetStdFile(nFile) + return &ansiReader{ + file: file, + fd: fd, + command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH), + buffer: make([]byte, 0), + } +} + +// Close closes the wrapped file. +func (ar *ansiReader) Close() (err error) { + return ar.file.Close() +} + +// Fd returns the file descriptor of the wrapped file. +func (ar *ansiReader) Fd() uintptr { + return ar.fd +} + +// Read reads up to len(p) bytes of translated input events into p. +func (ar *ansiReader) Read(p []byte) (int, error) { + if len(p) == 0 { + return 0, nil + } + + // Previously read bytes exist, read as much as we can and return + if len(ar.buffer) > 0 { + originalLength := len(ar.buffer) + copiedLength := copy(p, ar.buffer) + + if copiedLength == originalLength { + ar.buffer = make([]byte, 0, len(p)) + } else { + ar.buffer = ar.buffer[copiedLength:] + } + + return copiedLength, nil + } + + // Read and translate key events + events, err := readInputEvents(ar, len(p)) + if err != nil { + return 0, err + } else if len(events) == 0 { + return 0, nil + } + + keyBytes := translateKeyEvents(events, []byte(escapeSequence)) + + // Save excess bytes and right-size keyBytes + if len(keyBytes) > len(p) { + ar.buffer = keyBytes[len(p):] + keyBytes = keyBytes[:len(p)] + } else if len(keyBytes) == 0 { + return 0, nil + } + + copiedLength := copy(p, keyBytes) + if copiedLength != len(keyBytes) { + return 0, errors.New("unexpected copy length encountered") + } + + return copiedLength, nil +} + +// readInputEvents polls until at least one event is available. +func readInputEvents(ar *ansiReader, maxBytes int) ([]winterm.INPUT_RECORD, error) { + // Determine the maximum number of records to retrieve + // -- Cast around the type system to obtain the size of a single INPUT_RECORD. + // unsafe.Sizeof requires an expression vs. a type-reference; the casting + // tricks the type system into believing it has such an expression. + recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes))))) + countRecords := maxBytes / recordSize + if countRecords > ansiterm.MAX_INPUT_EVENTS { + countRecords = ansiterm.MAX_INPUT_EVENTS + } else if countRecords == 0 { + countRecords = 1 + } + + // Wait for and read input events + events := make([]winterm.INPUT_RECORD, countRecords) + nEvents := uint32(0) + eventsExist, err := winterm.WaitForSingleObject(ar.fd, winterm.WAIT_INFINITE) + if err != nil { + return nil, err + } + + if eventsExist { + err = winterm.ReadConsoleInput(ar.fd, events, &nEvents) + if err != nil { + return nil, err + } + } + + // Return a slice restricted to the number of returned records + return events[:nEvents], nil +} + +// KeyEvent Translation Helpers + +var arrowKeyMapPrefix = map[uint16]string{ + winterm.VK_UP: "%s%sA", + winterm.VK_DOWN: "%s%sB", + winterm.VK_RIGHT: "%s%sC", + winterm.VK_LEFT: "%s%sD", +} + +var keyMapPrefix = map[uint16]string{ + winterm.VK_UP: "\x1B[%sA", + winterm.VK_DOWN: "\x1B[%sB", + winterm.VK_RIGHT: "\x1B[%sC", + winterm.VK_LEFT: "\x1B[%sD", + winterm.VK_HOME: "\x1B[1%s~", // showkey shows ^[[1 + winterm.VK_END: "\x1B[4%s~", // showkey shows ^[[4 + winterm.VK_INSERT: "\x1B[2%s~", + winterm.VK_DELETE: "\x1B[3%s~", + winterm.VK_PRIOR: "\x1B[5%s~", + winterm.VK_NEXT: "\x1B[6%s~", + winterm.VK_F1: "", + winterm.VK_F2: "", + winterm.VK_F3: "\x1B[13%s~", + winterm.VK_F4: "\x1B[14%s~", + winterm.VK_F5: "\x1B[15%s~", + winterm.VK_F6: "\x1B[17%s~", + winterm.VK_F7: "\x1B[18%s~", + winterm.VK_F8: "\x1B[19%s~", + winterm.VK_F9: "\x1B[20%s~", + winterm.VK_F10: "\x1B[21%s~", + winterm.VK_F11: "\x1B[23%s~", + winterm.VK_F12: "\x1B[24%s~", +} + +// translateKeyEvents converts the input events into the appropriate ANSI string. +func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte { + var buffer bytes.Buffer + for _, event := range events { + if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 { + buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence)) + } + } + + return buffer.Bytes() +} + +// keyToString maps the given input event record to the corresponding string. +func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string { + if keyEvent.UnicodeChar == 0 { + return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence) + } + + _, alt, control := getControlKeys(keyEvent.ControlKeyState) + if control { + // TODO(azlinux): Implement following control sequences + // -D Signals the end of input from the keyboard; also exits current shell. + // -H Deletes the first character to the left of the cursor. Also called the ERASE key. + // -Q Restarts printing after it has been stopped with -s. + // -S Suspends printing on the screen (does not stop the program). + // -U Deletes all characters on the current line. Also called the KILL key. + // -E Quits current command and creates a core + + } + + // +Key generates ESC N Key + if !control && alt { + return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar)) + } + + return string(keyEvent.UnicodeChar) +} + +// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string. +func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string { + shift, alt, control := getControlKeys(controlState) + modifier := getControlKeysModifier(shift, alt, control) + + if format, ok := arrowKeyMapPrefix[key]; ok { + return fmt.Sprintf(format, escapeSequence, modifier) + } + + if format, ok := keyMapPrefix[key]; ok { + return fmt.Sprintf(format, modifier) + } + + return "" +} + +// getControlKeys extracts the shift, alt, and ctrl key states. +func getControlKeys(controlState uint32) (shift, alt, control bool) { + shift = 0 != (controlState & winterm.SHIFT_PRESSED) + alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED)) + control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED)) + return shift, alt, control +} + +// getControlKeysModifier returns the ANSI modifier for the given combination of control keys. +func getControlKeysModifier(shift, alt, control bool) string { + if shift && alt && control { + return ansiterm.KEY_CONTROL_PARAM_8 + } + if alt && control { + return ansiterm.KEY_CONTROL_PARAM_7 + } + if shift && control { + return ansiterm.KEY_CONTROL_PARAM_6 + } + if control { + return ansiterm.KEY_CONTROL_PARAM_5 + } + if shift && alt { + return ansiterm.KEY_CONTROL_PARAM_4 + } + if alt { + return ansiterm.KEY_CONTROL_PARAM_3 + } + if shift { + return ansiterm.KEY_CONTROL_PARAM_2 + } + return "" +} diff --git a/vendor/github.com/moby/term/windows/ansi_writer.go b/vendor/github.com/moby/term/windows/ansi_writer.go new file mode 100644 index 00000000..ccb5ef07 --- /dev/null +++ b/vendor/github.com/moby/term/windows/ansi_writer.go @@ -0,0 +1,56 @@ +// +build windows + +package windowsconsole + +import ( + "io" + "os" + + ansiterm "github.com/Azure/go-ansiterm" + "github.com/Azure/go-ansiterm/winterm" +) + +// ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation. +type ansiWriter struct { + file *os.File + fd uintptr + infoReset *winterm.CONSOLE_SCREEN_BUFFER_INFO + command []byte + escapeSequence []byte + inAnsiSequence bool + parser *ansiterm.AnsiParser +} + +// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a +// Windows console output handle. +func NewAnsiWriter(nFile int) io.Writer { + file, fd := winterm.GetStdFile(nFile) + info, err := winterm.GetConsoleScreenBufferInfo(fd) + if err != nil { + return nil + } + + parser := ansiterm.CreateParser("Ground", winterm.CreateWinEventHandler(fd, file)) + + return &ansiWriter{ + file: file, + fd: fd, + infoReset: info, + command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH), + escapeSequence: []byte(ansiterm.KEY_ESC_CSI), + parser: parser, + } +} + +func (aw *ansiWriter) Fd() uintptr { + return aw.fd +} + +// Write writes len(p) bytes from p to the underlying data stream. +func (aw *ansiWriter) Write(p []byte) (total int, err error) { + if len(p) == 0 { + return 0, nil + } + + return aw.parser.Parse(p) +} diff --git a/vendor/github.com/moby/term/windows/console.go b/vendor/github.com/moby/term/windows/console.go new file mode 100644 index 00000000..993694dd --- /dev/null +++ b/vendor/github.com/moby/term/windows/console.go @@ -0,0 +1,39 @@ +// +build windows + +package windowsconsole + +import ( + "os" + + "golang.org/x/sys/windows" +) + +// GetHandleInfo returns file descriptor and bool indicating whether the file is a console. +func GetHandleInfo(in interface{}) (uintptr, bool) { + switch t := in.(type) { + case *ansiReader: + return t.Fd(), true + case *ansiWriter: + return t.Fd(), true + } + + var inFd uintptr + var isTerminal bool + + if file, ok := in.(*os.File); ok { + inFd = file.Fd() + isTerminal = isConsole(inFd) + } + return inFd, isTerminal +} + +// IsConsole returns true if the given file descriptor is a Windows Console. +// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console. +// Deprecated: use golang.org/x/sys/windows.GetConsoleMode() or golang.org/x/term.IsTerminal() +var IsConsole = isConsole + +func isConsole(fd uintptr) bool { + var mode uint32 + err := windows.GetConsoleMode(windows.Handle(fd), &mode) + return err == nil +} diff --git a/vendor/github.com/moby/term/windows/doc.go b/vendor/github.com/moby/term/windows/doc.go new file mode 100644 index 00000000..54265fff --- /dev/null +++ b/vendor/github.com/moby/term/windows/doc.go @@ -0,0 +1,5 @@ +// These files implement ANSI-aware input and output streams for use by the Docker Windows client. +// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create +// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls. + +package windowsconsole diff --git a/vendor/github.com/moby/term/winsize.go b/vendor/github.com/moby/term/winsize.go new file mode 100644 index 00000000..1ef98d59 --- /dev/null +++ b/vendor/github.com/moby/term/winsize.go @@ -0,0 +1,20 @@ +// +build !windows + +package term + +import ( + "golang.org/x/sys/unix" +) + +// GetWinsize returns the window size based on the specified file descriptor. +func GetWinsize(fd uintptr) (*Winsize, error) { + uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) + ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel} + return ws, err +} + +// SetWinsize tries to set the specified window size for the specified file descriptor. +func SetWinsize(fd uintptr, ws *Winsize) error { + uws := &unix.Winsize{Row: ws.Height, Col: ws.Width, Xpixel: ws.x, Ypixel: ws.y} + return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, uws) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d9ccdfc8..951017a3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,3 +1,7 @@ +# github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 +## explicit; go 1.16 +github.com/Azure/go-ansiterm +github.com/Azure/go-ansiterm/winterm # github.com/Microsoft/go-winio v0.4.14 ## explicit; go 1.12 github.com/Microsoft/go-winio @@ -39,6 +43,7 @@ github.com/docker/docker/api/types/volume github.com/docker/docker/client github.com/docker/docker/errdefs github.com/docker/docker/pkg/stdcopy +github.com/docker/docker/pkg/term # github.com/docker/go-connections v0.4.0 ## explicit github.com/docker/go-connections/nat @@ -103,6 +108,8 @@ github.com/gogo/protobuf/proto # github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 ## explicit github.com/golang-collections/collections/stack +# github.com/google/go-cmp v0.5.5 +## explicit; go 1.8 # github.com/gookit/color v1.5.0 ## explicit; go 1.13 github.com/gookit/color @@ -142,8 +149,13 @@ github.com/mcuadros/go-lookup # github.com/mgutz/str v1.2.0 ## explicit github.com/mgutz/str +# github.com/micmonay/keybd_event v1.1.1 +## explicit; go 1.16 +github.com/micmonay/keybd_event # github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 ## explicit; go 1.13 +github.com/moby/term +github.com/moby/term/windows # github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c ## explicit # github.com/onsi/ginkgo v1.8.0