180 lines
3.1 KiB
Go
180 lines
3.1 KiB
Go
package info
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"golang.org/x/crypto/ssh"
|
|
"strconv"
|
|
"sync"
|
|
)
|
|
|
|
type Terminal struct {
|
|
// the chan
|
|
channel ssh.Channel
|
|
|
|
// The current prompt string
|
|
prompt string
|
|
|
|
width, height int
|
|
|
|
isListening bool
|
|
buffer []byte
|
|
|
|
lock *sync.Mutex
|
|
}
|
|
|
|
const (
|
|
keyCtrlC = 3
|
|
keyCtrlD = 4
|
|
keyEnter = 13
|
|
keyEscape = 27
|
|
keyReturn = 127
|
|
keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota
|
|
keyUp
|
|
keyDown
|
|
keyLeft
|
|
keyRight
|
|
keyAltLeft
|
|
keyAltRight
|
|
keyHome
|
|
keyEnd
|
|
keyDeleteWord
|
|
keyDeleteLine
|
|
keyClearScreen
|
|
keyPasteStart
|
|
keyPasteEnd
|
|
)
|
|
|
|
var (
|
|
CtrlC = errors.New("CtrlC")
|
|
CtrlD = errors.New("CtrlD")
|
|
)
|
|
|
|
func (term *Terminal) GetSize() (width int, height int) {
|
|
return term.width, term.height
|
|
}
|
|
|
|
func (term *Terminal) SetSize(width int, height int) {
|
|
term.width = width
|
|
term.height = height
|
|
}
|
|
|
|
func (term *Terminal) GetPrompt() string {
|
|
return term.prompt
|
|
}
|
|
|
|
func (term *Terminal) SetPrompt(prompt string) {
|
|
term.prompt = prompt
|
|
}
|
|
|
|
func (term *Terminal) ReadLine() (string, error) {
|
|
var content []byte
|
|
for {
|
|
data, _ := term.listen()
|
|
for _, b := range data {
|
|
fmt.Println(b)
|
|
switch []rune(data) {
|
|
case keyCtrlD:
|
|
if len(content) == 0 {
|
|
return "", CtrlD
|
|
}
|
|
case keyEnter:
|
|
term.channel.Write([]byte("\r\n" + term.prompt))
|
|
return string(data), nil
|
|
case keyReturn:
|
|
if len(content) - 1 > len(term.prompt) {
|
|
content = content[:len(content) - 1]
|
|
term.MoveCursor(0, 0, 1, 0)
|
|
term.channel.Write(content)
|
|
}
|
|
case keyRight:
|
|
fmt.Println("aaa")
|
|
default:
|
|
content = append(content, b)
|
|
term.channel.Write(data)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (term *Terminal) Write(data []byte) (n int, err error) {
|
|
return term.channel.Write(data)
|
|
}
|
|
|
|
func (term *Terminal) MoveCursor(up, down, left, right int) {
|
|
var m []rune
|
|
|
|
// 1 unit up can be expressed as ^[[A or ^[A
|
|
// 5 units up can be expressed as ^[[5A
|
|
|
|
if up == 1 {
|
|
m = append(m, keyEscape, '[', 'A')
|
|
} else if up > 1 {
|
|
m = append(m, keyEscape, '[')
|
|
m = append(m, []rune(strconv.Itoa(up))...)
|
|
m = append(m, 'A')
|
|
}
|
|
|
|
if down == 1 {
|
|
m = append(m, keyEscape, '[', 'B')
|
|
} else if down > 1 {
|
|
m = append(m, keyEscape, '[')
|
|
m = append(m, []rune(strconv.Itoa(down))...)
|
|
m = append(m, 'B')
|
|
}
|
|
|
|
if right == 1 {
|
|
m = append(m, keyEscape, '[', 'C')
|
|
} else if right > 1 {
|
|
m = append(m, keyEscape, '[')
|
|
m = append(m, []rune(strconv.Itoa(right))...)
|
|
m = append(m, 'C')
|
|
}
|
|
|
|
if left == 1 {
|
|
m = append(m, keyEscape, '[', 'D')
|
|
} else if left > 1 {
|
|
m = append(m, keyEscape, '[')
|
|
m = append(m, []rune(strconv.Itoa(left))...)
|
|
m = append(m, 'D')
|
|
}
|
|
|
|
term.Write([]byte(string(m)))
|
|
}
|
|
|
|
func (term *Terminal) ListenCtrlC() chan bool {
|
|
ctrlc := make(chan bool, 1)
|
|
go func() {
|
|
if term.isListening {
|
|
return
|
|
}
|
|
for {
|
|
data, err := term.listen()
|
|
if err == CtrlC {
|
|
ctrlc <- false
|
|
return
|
|
}
|
|
if term.isListening {
|
|
ctrlc <- false
|
|
term.buffer = data
|
|
}
|
|
}
|
|
}()
|
|
return ctrlc
|
|
}
|
|
|
|
func (term *Terminal) listen() ([]byte, error) {
|
|
buffer := make([]byte, 512)
|
|
n, err := term.channel.Read(buffer)
|
|
return buffer[:n], err
|
|
}
|
|
|
|
func NewTerminal(channel ssh.Channel) *Terminal {
|
|
return &Terminal{
|
|
channel: channel,
|
|
|
|
lock: &sync.Mutex{},
|
|
}
|
|
}
|
|
|