From 7f5c794e5fe20fa57c4a51c4f06dc406af5196ab Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Wed, 27 Feb 2019 17:01:02 +0100 Subject: [PATCH] add go-tty vendor --- go.mod | 2 + go.sum | 4 + .../github.com/mattn/go-runewidth/.travis.yml | 8 + vendor/github.com/mattn/go-tty/.travis.yml | 4 + vendor/github.com/mattn/go-tty/LICENSE | 21 ++ vendor/github.com/mattn/go-tty/README.md | 49 +++ vendor/github.com/mattn/go-tty/tty.go | 120 +++++++ vendor/github.com/mattn/go-tty/tty_bsd.go | 12 + vendor/github.com/mattn/go-tty/tty_linux.go | 8 + vendor/github.com/mattn/go-tty/tty_plan9.go | 63 ++++ vendor/github.com/mattn/go-tty/tty_unix.go | 130 +++++++ vendor/github.com/mattn/go-tty/tty_windows.go | 324 ++++++++++++++++++ 12 files changed, 745 insertions(+) create mode 100644 vendor/github.com/mattn/go-runewidth/.travis.yml create mode 100644 vendor/github.com/mattn/go-tty/.travis.yml create mode 100644 vendor/github.com/mattn/go-tty/LICENSE create mode 100644 vendor/github.com/mattn/go-tty/README.md create mode 100644 vendor/github.com/mattn/go-tty/tty.go create mode 100644 vendor/github.com/mattn/go-tty/tty_bsd.go create mode 100644 vendor/github.com/mattn/go-tty/tty_linux.go create mode 100644 vendor/github.com/mattn/go-tty/tty_plan9.go create mode 100644 vendor/github.com/mattn/go-tty/tty_unix.go create mode 100644 vendor/github.com/mattn/go-tty/tty_windows.go diff --git a/go.mod b/go.mod index 5abf32ecd..9deb50e28 100644 --- a/go.mod +++ b/go.mod @@ -115,6 +115,7 @@ require ( github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7 // indirect github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767 // indirect + github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f github.com/miekg/dns v1.1.1 // indirect github.com/mitchellh/cli v0.0.0-20170908181043-65fcae5817c8 github.com/mitchellh/copystructure v1.0.0 // indirect @@ -175,6 +176,7 @@ require ( golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect golang.org/x/tools v0.0.0-20190221204921-83362c3779f5 // indirect google.golang.org/api v0.0.0-20180818000503-e21acd801f91 + google.golang.org/appengine v1.4.0 // indirect google.golang.org/grpc v1.17.0 // indirect gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect diff --git a/go.sum b/go.sum index dcee98dcb..5465a3343 100644 --- a/go.sum +++ b/go.sum @@ -256,6 +256,8 @@ github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7 h1:owMyzMR4QR+jSdl github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767 h1:Nk2R0tWpD2RdkQ+53zE6kWnSGuhQyDlnOs2MPiqVubE= github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f h1:4P7Ul+TAnk92vTeVkXs6VLjmf1EhrYtDRa03PCYY6VM= +github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.1 h1:DVkblRdiScEnEr0LR9nTnEQqHYycjkXW9bOjd+2EL2o= @@ -403,6 +405,7 @@ google.golang.org/api v0.0.0-20180818000503-e21acd801f91 h1:MgYYgjaWMS2qQiDwCznf google.golang.org/api v0.0.0-20180818000503-e21acd801f91/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -437,5 +440,6 @@ gopkg.in/vmihailenco/msgpack.v2 v2.9.1 h1:kb0VV7NuIojvRfzwslQeP3yArBqJHW9tOl4t38 gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml new file mode 100644 index 000000000..5c9c2a30f --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - tip +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL diff --git a/vendor/github.com/mattn/go-tty/.travis.yml b/vendor/github.com/mattn/go-tty/.travis.yml new file mode 100644 index 000000000..63b9f7084 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/.travis.yml @@ -0,0 +1,4 @@ +language: go +sudo: false +go: + - tip diff --git a/vendor/github.com/mattn/go-tty/LICENSE b/vendor/github.com/mattn/go-tty/LICENSE new file mode 100644 index 000000000..e364750d2 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Yasuhiro Matsumoto + +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/mattn/go-tty/README.md b/vendor/github.com/mattn/go-tty/README.md new file mode 100644 index 000000000..6712eb49d --- /dev/null +++ b/vendor/github.com/mattn/go-tty/README.md @@ -0,0 +1,49 @@ +# go-tty + +Simple tty utility + +## Usage + +```go +tty, err := tty.Open() +if err != nil { + log.Fatal(err) +} +defer tty.Close() + +for { + r, err := tty.ReadRune() + if err != nil { + log.Fatal(err) + } + // handle key event +} +``` + +if you are on windows and want to display ANSI colors, use go-colorable. + +```go +tty, err := tty.Open() +if err != nil { + log.Fatal(err) +} +defer tty.Close() + +out := colorable.NewColorable(tty.Output()) + +fmt.Fprintln(out, "\x1b[2J") +``` + +## Installation + +``` +$ go get github.com/mattn/go-tty +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-tty/tty.go b/vendor/github.com/mattn/go-tty/tty.go new file mode 100644 index 000000000..8747638b3 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty.go @@ -0,0 +1,120 @@ +package tty + +import ( + "os" + "strings" + "unicode" +) + +func Open() (*TTY, error) { + return open() +} + +func (tty *TTY) Raw() (func() error, error) { + return tty.raw() +} + +func (tty *TTY) MustRaw() func() error { + f, err := tty.raw() + if err != nil { + panic(err.Error()) + } + return f +} + +func (tty *TTY) Buffered() bool { + return tty.buffered() +} + +func (tty *TTY) ReadRune() (rune, error) { + return tty.readRune() +} + +func (tty *TTY) Close() error { + return tty.close() +} + +func (tty *TTY) Size() (int, int, error) { + return tty.size() +} + +func (tty *TTY) Input() *os.File { + return tty.input() +} + +func (tty *TTY) Output() *os.File { + return tty.output() +} + +// Display types. +const ( + displayNone = iota + displayRune + displayMask +) + +func (tty *TTY) readString(displayType int) (string, error) { + rs := []rune{} +loop: + for { + r, err := tty.readRune() + if err != nil { + return "", err + } + switch r { + case 13: + break loop + case 8, 127: + if len(rs) > 0 { + rs = rs[:len(rs)-1] + if displayType != displayNone { + tty.Output().WriteString("\b \b") + } + } + default: + if unicode.IsPrint(r) { + rs = append(rs, r) + switch displayType { + case displayRune: + tty.Output().WriteString(string(r)) + case displayMask: + tty.Output().WriteString("*") + } + } + } + } + return string(rs), nil +} + +func (tty *TTY) ReadString() (string, error) { + defer tty.Output().WriteString("\n") + return tty.readString(displayRune) +} + +func (tty *TTY) ReadPassword() (string, error) { + defer tty.Output().WriteString("\n") + return tty.readString(displayMask) +} + +func (tty *TTY) ReadPasswordNoEcho() (string, error) { + defer tty.Output().WriteString("\n") + return tty.readString(displayNone) +} + +func (tty *TTY) ReadPasswordClear() (string, error) { + s, err := tty.readString(displayMask) + tty.Output().WriteString( + strings.Repeat("\b", len(s)) + + strings.Repeat(" ", len(s)) + + strings.Repeat("\b", len(s))) + return s, err +} + +type WINSIZE struct { + W int + H int +} + +func (tty *TTY) SIGWINCH() chan WINSIZE { + return tty.sigwinch() +} diff --git a/vendor/github.com/mattn/go-tty/tty_bsd.go b/vendor/github.com/mattn/go-tty/tty_bsd.go new file mode 100644 index 000000000..e0a51fc0d --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_bsd.go @@ -0,0 +1,12 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package tty + +import ( + "syscall" +) + +const ( + ioctlReadTermios = syscall.TIOCGETA + ioctlWriteTermios = syscall.TIOCSETA +) diff --git a/vendor/github.com/mattn/go-tty/tty_linux.go b/vendor/github.com/mattn/go-tty/tty_linux.go new file mode 100644 index 000000000..1b9e8cef6 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_linux.go @@ -0,0 +1,8 @@ +// +build linux + +package tty + +const ( + ioctlReadTermios = 0x5401 // syscall.TCGETS + ioctlWriteTermios = 0x5402 // syscall.TCSETS +) diff --git a/vendor/github.com/mattn/go-tty/tty_plan9.go b/vendor/github.com/mattn/go-tty/tty_plan9.go new file mode 100644 index 000000000..e880e5147 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_plan9.go @@ -0,0 +1,63 @@ +package tty + +import ( + "bufio" + "os" + "syscall" +) + +type TTY struct { + in *os.File + bin *bufio.Reader + out *os.File +} + +func open() (*TTY, error) { + tty := new(TTY) + + in, err := os.Open("/dev/cons") + if err != nil { + return nil, err + } + tty.in = in + tty.bin = bufio.NewReader(in) + + out, err := os.OpenFile("/dev/cons", syscall.O_WRONLY, 0) + if err != nil { + return nil, err + } + tty.out = out + + return tty, nil +} + +func (tty *TTY) buffered() bool { + return tty.bin.Buffered() > 0 +} + +func (tty *TTY) readRune() (rune, error) { + r, _, err := tty.bin.ReadRune() + return r, err +} + +func (tty *TTY) close() (err error) { + if err2 := tty.in.Close(); err2 != nil { + err = err2 + } + if err2 := tty.out.Close(); err2 != nil { + err = err2 + } + return +} + +func (tty *TTY) size() (int, int, error) { + return 80, 24, nil +} + +func (tty *TTY) input() *os.File { + return tty.in +} + +func (tty *TTY) output() *os.File { + return tty.out +} diff --git a/vendor/github.com/mattn/go-tty/tty_unix.go b/vendor/github.com/mattn/go-tty/tty_unix.go new file mode 100644 index 000000000..1ccdf5c95 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_unix.go @@ -0,0 +1,130 @@ +// +build !windows +// +build !plan9 + +package tty + +import ( + "bufio" + "os" + "os/signal" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +type TTY struct { + in *os.File + bin *bufio.Reader + out *os.File + termios syscall.Termios + ws chan WINSIZE + ss chan os.Signal +} + +func open() (*TTY, error) { + tty := new(TTY) + + in, err := os.Open("/dev/tty") + if err != nil { + return nil, err + } + tty.in = in + tty.bin = bufio.NewReader(in) + + out, err := os.OpenFile("/dev/tty", syscall.O_WRONLY, 0) + if err != nil { + return nil, err + } + tty.out = out + + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0); err != 0 { + return nil, err + } + newios := tty.termios + newios.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF + newios.Lflag &^= syscall.ECHO | syscall.ICANON /*| syscall.ISIG*/ + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newios)), 0, 0, 0); err != 0 { + return nil, err + } + + tty.ws = make(chan WINSIZE) + tty.ss = make(chan os.Signal, 1) + signal.Notify(tty.ss, syscall.SIGWINCH) + go func() { + for sig := range tty.ss { + switch sig { + case syscall.SIGWINCH: + if w, h, err := tty.size(); err == nil { + tty.ws <- WINSIZE{ + W: w, + H: h, + } + } + default: + } + } + }() + return tty, nil +} + +func (tty *TTY) buffered() bool { + return tty.bin.Buffered() > 0 +} + +func (tty *TTY) readRune() (rune, error) { + r, _, err := tty.bin.ReadRune() + return r, err +} + +func (tty *TTY) close() error { + close(tty.ss) + close(tty.ws) + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0) + return err +} + +func (tty *TTY) size() (int, int, error) { + var dim [4]uint16 + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim)), 0, 0, 0); err != 0 { + return -1, -1, err + } + return int(dim[1]), int(dim[0]), nil +} + +func (tty *TTY) input() *os.File { + return tty.in +} + +func (tty *TTY) output() *os.File { + return tty.out +} + +func (tty *TTY) raw() (func() error, error) { + termios, err := unix.IoctlGetTermios(int(tty.in.Fd()), ioctlReadTermios) + if err != nil { + return nil, err + } + + 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 := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, termios); err != nil { + return nil, err + } + + return func() error { + if err := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, termios); err != nil { + return err + } + return nil + }, nil +} + +func (tty *TTY) sigwinch() chan WINSIZE { + return tty.ws +} diff --git a/vendor/github.com/mattn/go-tty/tty_windows.go b/vendor/github.com/mattn/go-tty/tty_windows.go new file mode 100644 index 000000000..db5371845 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_windows.go @@ -0,0 +1,324 @@ +// +build windows + +package tty + +import ( + "os" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +const ( + rightAltPressed = 1 + leftAltPressed = 2 + rightCtrlPressed = 4 + leftCtrlPressed = 8 + shiftPressed = 0x0010 + ctrlPressed = rightCtrlPressed | leftCtrlPressed + altPressed = rightAltPressed | leftAltPressed +) + +const ( + enableProcessedInput = 0x1 + enableLineInput = 0x2 + enableEchoInput = 0x4 + enableWindowInput = 0x8 + enableMouseInput = 0x10 + enableInsertMode = 0x20 + enableQuickEditMode = 0x40 + enableExtendedFlag = 0x80 + + enableProcessedOutput = 1 + enableWrapAtEolOutput = 2 + + keyEvent = 0x1 + mouseEvent = 0x2 + windowBufferSizeEvent = 0x4 +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procAllocConsole = kernel32.NewProc("AllocConsole") + procSetStdHandle = kernel32.NewProc("SetStdHandle") + procGetStdHandle = kernel32.NewProc("GetStdHandle") + procSetConsoleScreenBufferSize = kernel32.NewProc("SetConsoleScreenBufferSize") + procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procWriteConsoleOutputCharacter = kernel32.NewProc("WriteConsoleOutputCharacterW") + procWriteConsoleOutputAttribute = kernel32.NewProc("WriteConsoleOutputAttribute") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procScrollConsoleScreenBuffer = kernel32.NewProc("ScrollConsoleScreenBufferW") +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} + +type inputRecord struct { + eventType word + _ [2]byte + event [16]byte +} + +type keyEventRecord struct { + keyDown int32 + repeatCount word + virtualKeyCode word + virtualScanCode word + unicodeChar wchar + controlKeyState dword +} + +type windowBufferSizeRecord struct { + size coord +} + +type mouseEventRecord struct { + mousePos coord + buttonState dword + controlKeyState dword + eventFlags dword +} + +type charInfo struct { + unicodeChar wchar + attributes word +} + +type TTY struct { + in *os.File + out *os.File + st uint32 + rs []rune + ws chan WINSIZE +} + +func readConsoleInput(fd uintptr, record *inputRecord) (err error) { + var w uint32 + r1, _, err := procReadConsoleInput.Call(fd, uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&w))) + if r1 == 0 { + return err + } + return nil +} + +func open() (*TTY, error) { + tty := new(TTY) + if false && isatty.IsTerminal(os.Stdin.Fd()) { + tty.in = os.Stdin + } else { + in, err := syscall.Open("CONIN$", syscall.O_RDWR, 0) + if err != nil { + return nil, err + } + + tty.in = os.NewFile(uintptr(in), "/dev/tty") + } + + if isatty.IsTerminal(os.Stdout.Fd()) { + tty.out = os.Stdout + } else { + procAllocConsole.Call() + out, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if err != nil { + return nil, err + } + + tty.out = os.NewFile(uintptr(out), "/dev/tty") + } + + h := tty.in.Fd() + var st uint32 + r1, _, err := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&st))) + if r1 == 0 { + return nil, err + } + tty.st = st + + st &^= enableEchoInput + st &^= enableInsertMode + st &^= enableLineInput + st &^= enableMouseInput + st &^= enableWindowInput + st &^= enableExtendedFlag + st &^= enableQuickEditMode + st &^= enableProcessedInput + + // ignore error + procSetConsoleMode.Call(h, uintptr(st)) + + tty.ws = make(chan WINSIZE) + + return tty, nil +} + +func (tty *TTY) buffered() bool { + return len(tty.rs) > 0 +} + +func (tty *TTY) readRune() (rune, error) { + if len(tty.rs) > 0 { + r := tty.rs[0] + tty.rs = tty.rs[1:] + return r, nil + } + var ir inputRecord + err := readConsoleInput(tty.in.Fd(), &ir) + if err != nil { + return 0, err + } + + switch ir.eventType { + case windowBufferSizeEvent: + wr := (*windowBufferSizeRecord)(unsafe.Pointer(&ir.event)) + tty.ws <- WINSIZE{ + W: int(wr.size.x), + H: int(wr.size.y), + } + case keyEvent: + kr := (*keyEventRecord)(unsafe.Pointer(&ir.event)) + if kr.keyDown != 0 { + if kr.controlKeyState&altPressed != 0 && kr.unicodeChar > 0 { + tty.rs = []rune{rune(kr.unicodeChar)} + return rune(0x1b), nil + } + if kr.unicodeChar > 0 { + if kr.controlKeyState&shiftPressed != 0 { + switch kr.unicodeChar { + case 0x09: + tty.rs = []rune{0x5b, 0x5a} + return rune(0x1b), nil + } + } + return rune(kr.unicodeChar), nil + } + vk := kr.virtualKeyCode + switch vk { + case 0x21: // page-up + tty.rs = []rune{0x5b, 0x35, 0x7e} + return rune(0x1b), nil + case 0x22: // page-down + tty.rs = []rune{0x5b, 0x36, 0x7e} + return rune(0x1b), nil + case 0x23: // end + tty.rs = []rune{0x5b, 0x46} + return rune(0x1b), nil + case 0x24: // home + tty.rs = []rune{0x5b, 0x48} + return rune(0x1b), nil + case 0x25: // left + tty.rs = []rune{0x5b, 0x44} + return rune(0x1b), nil + case 0x26: // up + tty.rs = []rune{0x5b, 0x41} + return rune(0x1b), nil + case 0x27: // right + tty.rs = []rune{0x5b, 0x43} + return rune(0x1b), nil + case 0x28: // down + tty.rs = []rune{0x5b, 0x42} + return rune(0x1b), nil + case 0x2e: // delete + tty.rs = []rune{0x5b, 0x33, 0x7e} + return rune(0x1b), nil + case 0x70, 0x71, 0x72, 0x73: // F1,F2,F3,F4 + tty.rs = []rune{0x5b, 0x4f, rune(vk) - 0x20} + return rune(0x1b), nil + case 0x074, 0x75, 0x76, 0x77: // F5,F6,F7,F8 + tty.rs = []rune{0x5b, 0x31, rune(vk) - 0x3f, 0x7e} + return rune(0x1b), nil + case 0x78, 0x79: // F9,F10 + tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x48, 0x7e} + return rune(0x1b), nil + case 0x7a, 0x7b: // F11,F12 + tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x47, 0x7e} + return rune(0x1b), nil + } + return 0, nil + } + } + return 0, nil +} + +func (tty *TTY) close() error { + close(tty.ws) + procSetConsoleMode.Call(tty.in.Fd(), uintptr(tty.st)) + return nil +} + +func (tty *TTY) size() (int, int, error) { + var csbi consoleScreenBufferInfo + r1, _, err := procGetConsoleScreenBufferInfo.Call(tty.out.Fd(), uintptr(unsafe.Pointer(&csbi))) + if r1 == 0 { + return 0, 0, err + } + return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1), nil +} + +func (tty *TTY) input() *os.File { + return tty.in +} + +func (tty *TTY) output() *os.File { + return tty.out +} + +func (tty *TTY) raw() (func() error, error) { + var st uint32 + r1, _, err := procGetConsoleMode.Call(tty.in.Fd(), uintptr(unsafe.Pointer(&st))) + if r1 == 0 { + return nil, err + } + mode := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) + r1, _, err = procSetConsoleMode.Call(tty.in.Fd(), uintptr(mode)) + if r1 == 0 { + return nil, err + } + return func() error { + r1, _, err := procSetConsoleMode.Call(tty.in.Fd(), uintptr(st)) + if r1 == 0 { + return err + } + return nil + }, nil +} + +func (tty *TTY) sigwinch() chan WINSIZE { + return tty.ws +}