M go.mod => go.mod +2 -1
@@ 11,7 11,8 @@ require (
github.com/stretchr/testify v1.8.3
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/image v0.9.0
- golang.org/x/sys v0.10.0
+ golang.org/x/sys v0.14.0
+ golang.org/x/term v0.14.0
)
require (
M go.sum => go.sum +4 -2
@@ 40,11 40,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
-golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
M image.go => image.go +1 -1
@@ 114,7 114,7 @@ func (k *KittyImage) Draw(win Window) {
// Destroy deletes this image from memory
func (k *KittyImage) Destroy() {
- fmt.Fprintf(k.vx.console, "\x1B_Ga=d,d=I,i=%d\x1B\\", k.id)
+ fmt.Fprintf(k.vx.pty, "\x1B_Ga=d,d=I,i=%d\x1B\\", k.id)
}
func (k *KittyImage) CellSize() (w int, h int) {
A term/pty.go => term/pty.go +32 -0
@@ 0,0 1,32 @@
+package term
+
+import (
+ "io"
+ "os"
+)
+
+type Pty interface {
+ io.ReadWriteCloser
+
+ MakeRaw() error
+ Restore() error
+ // Size reports the Pty's current size
+ Size() (Size, error)
+ // Notify reports terminal events which can't otherwise be included in
+ // the input stream. Currently only size change signals will be sent.
+ // The provided channel should have a buffer of at least 1: signals will
+ // be dropped if they cannot immediately be sent to the channel
+ Notify(chan os.Signal)
+}
+
+// OpenPty opens a handle to the controlling terminal's pty
+func OpenPty() (Pty, error) {
+ return openPty()
+}
+
+type Size struct {
+ Row int
+ Col int
+ XPixel int
+ YPixel int
+}
A term/pty_unix.go => term/pty_unix.go +71 -0
@@ 0,0 1,71 @@
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
+
+package term
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+ "golang.org/x/term"
+)
+
+// pty is a unix pseudo-terminal
+type pty struct {
+ state *term.State
+ fd int
+}
+
+func openPty() (Pty, error) {
+ fd, err := syscall.Open("/dev/tty", os.O_RDWR, 0)
+ if err != nil {
+ return nil, err
+ }
+ pty := &pty{
+ fd: fd,
+ }
+ return pty, nil
+}
+
+func (p *pty) Read(b []byte) (n int, err error) {
+ return syscall.Read(p.fd, b)
+}
+
+func (p *pty) Write(b []byte) (n int, err error) {
+ return syscall.Write(p.fd, b)
+}
+
+func (p *pty) Close() error {
+ return syscall.Close(p.fd)
+}
+
+func (p *pty) MakeRaw() error {
+ termios, err := term.MakeRaw(p.fd)
+ if err != nil {
+ return err
+ }
+ p.state = termios
+ return nil
+}
+
+func (p *pty) Restore() error {
+ return term.Restore(p.fd, p.state)
+}
+
+func (p *pty) Size() (Size, error) {
+ ws, err := unix.IoctlGetWinsize(p.fd, unix.TIOCGWINSZ)
+ if err != nil {
+ return Size{}, err
+ }
+ return Size{
+ Row: int(ws.Row),
+ Col: int(ws.Col),
+ XPixel: int(ws.Xpixel),
+ YPixel: int(ws.Ypixel),
+ }, nil
+}
+
+func (p *pty) Notify(ch chan os.Signal) {
+ signal.Notify(ch, syscall.SIGWINCH)
+}
A term/pty_windows.go => term/pty_windows.go +64 -0
@@ 0,0 1,64 @@
+package term
+
+import (
+ "os"
+ "syscall"
+)
+
+var k32 = syscall.NewLazyDLL("kernel32.dll")
+
+type conPty struct {
+ stdin syscall.Handle
+ stdout syscall.Handle
+}
+
+func openPty() (Pty, error) {
+ stdin, err := syscall.Open("CONIN$", os.O_RDWR, 0)
+ if err != nil {
+ return nil, err
+ }
+ stdout, err := syscall.Open("CONOUT$", os.O_RDWR, 0)
+ if err != nil {
+ return nil, err
+ }
+ pty := &conPty{
+ stdin: stdin,
+ stdout: stdout,
+ }
+ return pty, nil
+}
+
+func (pty *conPty) Read(p []byte) (n int, err error) {
+ panic("not implemented") // TODO: Implement
+}
+
+func (pty *conPty) Write(p []byte) (n int, err error) {
+ panic("not implemented") // TODO: Implement
+}
+
+func (pty *conPty) Close() error {
+ syscall.Close(pty.stdin)
+ syscall.Close(pty.stdout)
+ return nil
+}
+
+func (pty *conPty) MakeRaw() error {
+ panic("not implemented") // TODO: Implement
+}
+
+func (pty *conPty) Restore() error {
+ panic("not implemented") // TODO: Implement
+}
+
+// Size reports the Pty's current size
+func (pty *conPty) Size() (term.Size, error) {
+ panic("not implemented") // TODO: Implement
+}
+
+// Notify reports terminal events which can't otherwise be included in
+// the input stream. Currently only size change signals will be sent.
+// The provided channel should have a buffer of at least 1: signals will
+// be dropped if they cannot immediately be sent to the channel
+func (pty *conPty) Notify(_ chan os.Signal) {
+ panic("not implemented") // TODO: Implement
+}
M vaxis.go => vaxis.go +21 -23
@@ 13,12 13,12 @@ import (
"sync/atomic"
"time"
- "github.com/containerd/console"
"github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
"git.sr.ht/~rockorager/vaxis/ansi"
"git.sr.ht/~rockorager/vaxis/log"
+ "git.sr.ht/~rockorager/vaxis/term"
)
type capabilities struct {
@@ 58,8 58,9 @@ type Options struct {
}
type Vaxis struct {
- queue chan Event
- console console.Console
+ queue chan Event
+ // console console.Console
+ pty term.Pty
tw *writer
screenNext *screen
screenLast *screen
@@ 1025,27 1026,24 @@ func (vx *Vaxis) Suspend() error {
vx.exitAltScreen()
signal.Stop(vx.chSigKill)
signal.Stop(vx.chSigWinSz)
- vx.console.Reset()
+ vx.pty.Restore()
+ // vx.console.Reset()
return nil
}
-// makeRaw opens the /dev/tty device, makes it raw, and starts an input parser
+// openTty opens the /dev/tty device, makes it raw, and starts an input parser
func (vx *Vaxis) openTty() error {
- for _, s := range []*os.File{os.Stderr, os.Stdout, os.Stdin} {
- if c, err := console.ConsoleFromFile(s); err == nil {
- vx.console = c
- break
- }
- }
- if vx.console == nil {
- return console.ErrNotAConsole
+ pty, err := term.OpenPty()
+ if err != nil {
+ return err
}
- err := vx.console.SetRaw()
+ vx.pty = pty
+ err = vx.pty.MakeRaw()
if err != nil {
return err
}
vx.tw = newWriter(vx)
- parser := ansi.NewParser(vx.console)
+ parser := ansi.NewParser(vx.pty)
go func() {
defer func() {
if err := recover(); err != nil {
@@ 1083,7 1081,7 @@ func (vx *Vaxis) Resume() error {
// if err != nil {
// return err
// }
- err := vx.console.SetRaw()
+ err := vx.pty.MakeRaw()
if err != nil {
return err
}
@@ 1122,7 1120,7 @@ func (vx *Vaxis) showCursor() string {
func (vx *Vaxis) CursorPosition() (row int, col int) {
// DSRCPR - reports cursor position
atomicStore(&vx.reqCursorPos, true)
- _, _ = io.WriteString(vx.console, dsrcpr)
+ _, _ = io.WriteString(vx.pty, dsrcpr)
timeout := time.NewTimer(50 * time.Millisecond)
select {
case <-timeout.C:
@@ 1157,7 1155,7 @@ func (vx *Vaxis) cursorStyle() string {
// ClipboardPush copies the provided string to the system clipboard
func (vx *Vaxis) ClipboardPush(s string) {
b64 := base64.StdEncoding.EncodeToString([]byte(s))
- _, _ = io.WriteString(vx.console, tparm(osc52put, b64))
+ _, _ = io.WriteString(vx.pty, tparm(osc52put, b64))
}
// ClipboardPop requests the content from the system clipboard. ClipboardPop works by
@@ 1166,7 1164,7 @@ func (vx *Vaxis) ClipboardPush(s string) {
// a context to set a deadline for this function to return. An error will be
// returned if the context is cancelled.
func (vx *Vaxis) ClipboardPop(ctx context.Context) (string, error) {
- _, _ = io.WriteString(vx.console, osc52pop)
+ _, _ = io.WriteString(vx.pty, osc52pop)
select {
case str := <-vx.chClipboard:
return str, nil
@@ 1179,20 1177,20 @@ func (vx *Vaxis) ClipboardPop(ctx context.Context) (string, error) {
// string, OSC9 will be used - otherwise osc777 is used
func (vx *Vaxis) Notify(title string, body string) {
if title == "" {
- _, _ = io.WriteString(vx.console, tparm(osc9notify, body))
+ _, _ = io.WriteString(vx.pty, tparm(osc9notify, body))
return
}
- _, _ = io.WriteString(vx.console, tparm(osc777notify, title, body))
+ _, _ = io.WriteString(vx.pty, tparm(osc777notify, title, body))
}
// SetTitle sets the terminal's title via OSC 2
func (vx *Vaxis) SetTitle(s string) {
- _, _ = io.WriteString(vx.console, tparm(setTitle, s))
+ _, _ = io.WriteString(vx.pty, tparm(setTitle, s))
}
// Bell sends a BEL control signal to the terminal
func (vx *Vaxis) Bell() {
- _, _ = vx.console.Write([]byte{0x07})
+ _, _ = vx.pty.Write([]byte{0x07})
}
// advance returns the extra amount to advance the column by when rendering
M vaxis_unix.go => vaxis_unix.go +6 -8
@@ 10,13 10,10 @@ import (
"time"
"git.sr.ht/~rockorager/vaxis/log"
- "golang.org/x/sys/unix"
)
func (vx *Vaxis) setupSignals() {
- signal.Notify(vx.chSigWinSz,
- syscall.SIGWINCH,
- )
+ vx.pty.Notify(vx.chSigWinSz)
signal.Notify(vx.chSigKill,
// kill signals
syscall.SIGABRT,
@@ 34,7 31,7 @@ func (vx *Vaxis) setupSignals() {
func (vx *Vaxis) reportWinsize() (Resize, error) {
if vx.caps.reportSizeChars && vx.caps.reportSizePixels {
log.Trace("requesting screen size from terminal")
- io.WriteString(vx.console, textAreaSize)
+ io.WriteString(vx.pty, textAreaSize)
deadline := time.NewTimer(100 * time.Millisecond)
select {
case <-deadline.C:
@@ 44,14 41,15 @@ func (vx *Vaxis) reportWinsize() (Resize, error) {
}
}
log.Trace("requesting screen size from ioctl")
- ws, err := unix.IoctlGetWinsize(int(vx.console.Fd()), unix.TIOCGWINSZ)
+ ws, err := vx.pty.Size()
+ // ws, err := unix.IoctlGetWinsize(int(vx.pty.Fd()), unix.TIOCGWINSZ)
if err != nil {
return Resize{}, err
}
return Resize{
Cols: int(ws.Col),
Rows: int(ws.Row),
- XPixel: int(ws.Xpixel),
- YPixel: int(ws.Ypixel),
+ XPixel: int(ws.XPixel),
+ YPixel: int(ws.YPixel),
}, nil
}
M writer.go => writer.go +1 -1
@@ 18,7 18,7 @@ type writer struct {
func newWriter(vx *Vaxis) *writer {
return &writer{
buf: bytes.NewBuffer(make([]byte, 8192)),
- w: vx.console,
+ w: vx.pty,
vx: vx,
}
}