2024-06-05 11:45:03 +03:00
|
|
|
package tx
|
|
|
|
|
|
|
|
import "golang.org/x/term"
|
2024-07-26 21:13:04 +03:00
|
|
|
import "surdeus.su/core/cli/aes"
|
2024-06-05 11:45:03 +03:00
|
|
|
import "bufio"
|
|
|
|
import "fmt"
|
|
|
|
import "os"
|
2024-07-26 22:27:34 +03:00
|
|
|
import "os/signal"
|
|
|
|
import "syscall"
|
|
|
|
|
|
|
|
type Signal = os.Signal
|
|
|
|
const (
|
|
|
|
SignalWinChange = syscall.SIGWINCH
|
|
|
|
)
|
2024-06-05 11:45:03 +03:00
|
|
|
|
|
|
|
type Terminal struct {
|
|
|
|
x *term.Terminal
|
|
|
|
file *os.File
|
|
|
|
input *bufio.Reader
|
|
|
|
fd int
|
|
|
|
prevState *term.State
|
2024-07-26 22:27:34 +03:00
|
|
|
|
|
|
|
signals chan Signal
|
|
|
|
keys chan Key
|
2024-06-05 11:45:03 +03:00
|
|
|
}
|
|
|
|
|
2024-06-05 12:49:23 +03:00
|
|
|
func NewTerminal(input *os.File) (*Terminal, error) {
|
2024-06-05 11:45:03 +03:00
|
|
|
if input == nil {
|
2024-06-05 12:49:23 +03:00
|
|
|
return nil, ErrNoInputProvided
|
2024-06-05 11:45:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fd := int(input.Fd())
|
|
|
|
if !term.IsTerminal(fd) {
|
2024-06-05 12:49:23 +03:00
|
|
|
return nil, ErrNotTerminal
|
2024-06-05 11:45:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ret := Terminal{}
|
|
|
|
ret.file = input
|
|
|
|
ret.input = bufio.NewReader(input)
|
|
|
|
ret.fd = fd
|
|
|
|
ret.x = term.NewTerminal(input, "")
|
|
|
|
|
2024-06-05 12:49:23 +03:00
|
|
|
return &ret, nil
|
2024-06-05 11:45:03 +03:00
|
|
|
}
|
|
|
|
|
2024-07-26 22:27:34 +03:00
|
|
|
func (t *Terminal) MakeRaw() (chan Key, chan Signal, error) {
|
2024-06-05 11:45:03 +03:00
|
|
|
state, err := term.MakeRaw(t.fd)
|
|
|
|
if err != nil {
|
2024-07-26 22:27:34 +03:00
|
|
|
return nil, nil, err
|
2024-06-05 11:45:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
t.prevState = state
|
2024-07-26 22:27:34 +03:00
|
|
|
t.signals = make(chan Signal, 1)
|
|
|
|
signal.Notify(
|
|
|
|
t.signals,
|
|
|
|
syscall.SIGWINCH,
|
|
|
|
)
|
|
|
|
t.keys = make(chan Key, 1)
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
r, _, _ := t.input.ReadRune()
|
|
|
|
t.keys <- Key(r)
|
|
|
|
}
|
|
|
|
} ()
|
|
|
|
|
|
|
|
return t.keys, t.signals, nil
|
2024-06-05 11:45:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) Restore() error {
|
2024-07-26 21:13:04 +03:00
|
|
|
t.DisableAltBuffer()
|
2024-06-05 12:49:23 +03:00
|
|
|
if t == nil || t.prevState == nil {
|
2024-06-05 11:45:03 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err := term.Restore(t.fd, t.prevState)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-07-26 22:27:34 +03:00
|
|
|
func (t *Terminal) Size() (int, int, error) {
|
2024-06-05 11:45:03 +03:00
|
|
|
return term.GetSize(t.fd)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) ReadLine() (string, error) {
|
|
|
|
return t.x.ReadLine()
|
|
|
|
}
|
|
|
|
|
2024-07-26 22:27:34 +03:00
|
|
|
/*
|
2024-06-05 11:45:03 +03:00
|
|
|
func (t *Terminal) ReadKey() (Key, int, error) {
|
|
|
|
r, size, err := t.input.ReadRune()
|
|
|
|
return Key(r), size, err
|
|
|
|
}
|
2024-07-26 22:27:34 +03:00
|
|
|
*/
|
2024-06-05 11:45:03 +03:00
|
|
|
|
|
|
|
func (t *Terminal) Write(bts []byte) (int, error) {
|
|
|
|
return t.x.Write(bts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) Printf(
|
|
|
|
format string, v ...any,
|
|
|
|
) (int, error) {
|
|
|
|
str := fmt.Sprintf(format, v...)
|
|
|
|
return t.Write([]byte(str))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) Print(v ...any) (int, error) {
|
|
|
|
str := fmt.Sprint(v...)
|
|
|
|
return t.Write([]byte(str))
|
|
|
|
}
|
2024-07-26 21:13:04 +03:00
|
|
|
|
|
|
|
func (t *Terminal) MoveCursorRight(n int) {
|
|
|
|
t.Printf(aes.MoveCursorRightFormat, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) MoveCursorLeft(n int) {
|
|
|
|
t.Printf(aes.MoveCursorLeftFormat, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) MoveCursorUp(n int) {
|
|
|
|
t.Printf(aes.MoveCursorUpFormat, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) MoveCursorDown(n int) {
|
|
|
|
t.Printf(aes.MoveCursorDownFormat, n)
|
|
|
|
}
|
|
|
|
|
2024-08-19 05:47:21 +03:00
|
|
|
func (t *Terminal) MoveCursorBy(xd, yd int) {
|
|
|
|
if xd != 0 {
|
|
|
|
if yd < 0 {
|
|
|
|
t.MoveCursorLeft(xd)
|
|
|
|
} else {
|
|
|
|
t.MoveCursorRight(xd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if yd != 0 {
|
|
|
|
if yd < 0 {
|
|
|
|
t.MoveCursorUp(yd)
|
|
|
|
} else {
|
|
|
|
t.MoveCursorDown(yd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) MoveCursorTo(x, y int) {
|
|
|
|
t.Printf(aes.MoveCursorToFormat, y, x)
|
|
|
|
}
|
|
|
|
|
2024-07-26 22:27:34 +03:00
|
|
|
func (t *Terminal) MoveCursorHome() {
|
|
|
|
t.Printf("%s", aes.MoveCursorHome)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) SaveCursor() {
|
2024-08-19 05:47:21 +03:00
|
|
|
t.Printf("%s", aes.SaveCursorPositionSCO)
|
2024-07-26 22:27:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) UndoCursor() {
|
2024-08-19 05:47:21 +03:00
|
|
|
t.Printf("%s", aes.UndoCursorPositionSCO)
|
2024-07-26 22:27:34 +03:00
|
|
|
}
|
|
|
|
|
2024-07-26 21:13:04 +03:00
|
|
|
func (t *Terminal) NextLine(n int) {
|
|
|
|
t.Printf(
|
|
|
|
aes.MoveCursorToBeginOfNextLineDownFormat,
|
|
|
|
n,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-08-19 05:47:21 +03:00
|
|
|
func (t *Terminal) EraseLine() {
|
|
|
|
t.Print(aes.EraseEntireLine)
|
|
|
|
}
|
|
|
|
|
2024-07-26 21:13:04 +03:00
|
|
|
func (t *Terminal) EnableAltBuffer() {
|
|
|
|
t.Printf("%s", aes.EnableAltBuffer)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terminal) DisableAltBuffer() {
|
|
|
|
t.Printf("%s", aes.DisableAltBuffer)
|
|
|
|
}
|
|
|
|
|
2024-07-26 22:27:34 +03:00
|
|
|
func (t *Terminal) ClearScreen() {
|
|
|
|
t.Write([]byte(aes.EraseEntireScreen))
|
|
|
|
}
|