add go-tty vendor
This commit is contained in:
parent
239a0c633f
commit
7f5c794e5f
2
go.mod
2
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
|
||||
|
|
4
go.sum
4
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=
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- tip
|
|
@ -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.
|
|
@ -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 <a href="https://github.com/mattn/go-colorable">go-colorable</a>.
|
||||
|
||||
```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)
|
|
@ -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()
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package tty
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
ioctlReadTermios = syscall.TIOCGETA
|
||||
ioctlWriteTermios = syscall.TIOCSETA
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
// +build linux
|
||||
|
||||
package tty
|
||||
|
||||
const (
|
||||
ioctlReadTermios = 0x5401 // syscall.TCGETS
|
||||
ioctlWriteTermios = 0x5402 // syscall.TCSETS
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue