-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1c59f1a
commit c321e5c
Showing
9 changed files
with
421 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package xpty | ||
|
||
import ( | ||
"os/exec" | ||
|
||
"github.com/charmbracelet/x/conpty" | ||
) | ||
|
||
// ConPty is a Windows console pty. | ||
type ConPty struct { | ||
*conpty.ConPty | ||
} | ||
|
||
var _ Pty = &ConPty{} | ||
|
||
// NewConPty creates a new ConPty. | ||
func NewConPty(width, height int, opts ...PtyOption) (*ConPty, error) { | ||
var opt Options | ||
for _, o := range opts { | ||
o(opt) | ||
} | ||
|
||
c, err := conpty.New(width, height, opt.Flags) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &ConPty{c}, nil | ||
} | ||
|
||
// Name returns the name of the ConPty. | ||
func (c *ConPty) Name() string { | ||
return "windows-pty" | ||
} | ||
|
||
// Start starts a command on the ConPty. | ||
// This is a wrapper around conpty.Spawn. | ||
func (c *ConPty) Start(cmd *exec.Cmd) error { | ||
return c.start(cmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
//go:build !windows | ||
// +build !windows | ||
|
||
package xpty | ||
|
||
import "os/exec" | ||
|
||
func (c *ConPty) start(*exec.Cmd) error { | ||
return ErrUnsupported | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
//go:build windows | ||
// +build windows | ||
|
||
package xpty | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"syscall" | ||
|
||
"golang.org/x/sys/windows" | ||
) | ||
|
||
func (c *ConPty) start(cmd *exec.Cmd) error { | ||
pid, proc, err := c.ConPty.Spawn(cmd.Path, cmd.Args, &syscall.ProcAttr{ | ||
Dir: cmd.Dir, | ||
Env: cmd.Env, | ||
Sys: cmd.SysProcAttr, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
cmd.Process, err = os.FindProcess(pid) | ||
if err != nil { | ||
// If we can't find the process via os.FindProcess, terminate the | ||
// process as that's what we rely on for all further operations on the | ||
// object. | ||
if tErr := windows.TerminateProcess(windows.Handle(proc), 1); tErr != nil { | ||
return fmt.Errorf("failed to terminate process after process not found: %w", tErr) | ||
} | ||
return fmt.Errorf("failed to find process after starting: %w", err) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
module github.com/charmbracelet/x/xpty | ||
|
||
go 1.18 | ||
|
||
require ( | ||
github.com/charmbracelet/x/conpty v0.1.0 | ||
github.com/charmbracelet/x/term v0.1.0 | ||
github.com/charmbracelet/x/termios v0.1.0 | ||
github.com/creack/pty v1.1.21 | ||
golang.org/x/sys v0.20.0 | ||
) | ||
|
||
require ( | ||
github.com/charmbracelet/x/ansi v0.1.0 // indirect | ||
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 // indirect | ||
github.com/charmbracelet/x/input v0.1.0 // indirect | ||
github.com/charmbracelet/x/windows v0.1.0 // indirect | ||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect | ||
github.com/muesli/cancelreader v0.2.2 // indirect | ||
github.com/rivo/uniseg v0.4.7 // indirect | ||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
github.com/charmbracelet/x/ansi v0.1.0 h1:o4NbQQCoVgbLpD5RC1cI687baoLwrLZyCOTGlF0gne4= | ||
github.com/charmbracelet/x/ansi v0.1.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= | ||
github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= | ||
github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= | ||
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= | ||
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= | ||
github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= | ||
github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28= | ||
github.com/charmbracelet/x/term v0.1.0 h1:yOhOuAKvqAIIrUg2TDHnURdxVXOBQIjMhfgg/avR6j0= | ||
github.com/charmbracelet/x/term v0.1.0/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= | ||
github.com/charmbracelet/x/termios v0.1.0 h1:y4rjAHeFksBAfGbkRDmVinMg7x7DELIGAFbdNvxg97k= | ||
github.com/charmbracelet/x/termios v0.1.0/go.mod h1:H/EVv/KRnrYjz+fCYa9bsKdqF3S8ouDK0AZEbG7r+/U= | ||
github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4= | ||
github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= | ||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= | ||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= | ||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= | ||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= | ||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= | ||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= | ||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= | ||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | ||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= | ||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= | ||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= | ||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= | ||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package xpty | ||
|
||
import ( | ||
"os" | ||
"os/exec" | ||
|
||
"github.com/creack/pty" | ||
) | ||
|
||
// UnixPty represents a classic Unix PTY (pseudo-terminal). | ||
type UnixPty struct { | ||
master, slave *os.File | ||
} | ||
|
||
var _ Pty = &UnixPty{} | ||
|
||
// NewUnixPty creates a new Unix PTY. | ||
func NewUnixPty(width, height int, _ ...PtyOption) (*UnixPty, error) { | ||
ptm, pts, err := pty.Open() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
p := &UnixPty{ | ||
master: ptm, | ||
slave: pts, | ||
} | ||
|
||
if width >= 0 && height >= 0 { | ||
if err := p.Resize(width, height); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
return p, nil | ||
} | ||
|
||
// Close implements XPTY. | ||
func (p *UnixPty) Close() (err error) { | ||
defer func() { | ||
serr := p.slave.Close() | ||
if err == nil { | ||
err = serr | ||
} | ||
}() | ||
if err := p.master.Close(); err != nil { | ||
return err | ||
} | ||
return | ||
} | ||
|
||
// Fd implements XPTY. | ||
func (p *UnixPty) Fd() uintptr { | ||
return p.master.Fd() | ||
} | ||
|
||
// Name implements XPTY. | ||
func (p *UnixPty) Name() string { | ||
return p.master.Name() | ||
} | ||
|
||
// SlaveName returns the name of the slave PTY. | ||
// This is usually used for remote sessions to identify the running TTY. You | ||
// can find this in SSH sessions defined as $SSH_TTY. | ||
func (p *UnixPty) SlaveName() string { | ||
return p.slave.Name() | ||
} | ||
|
||
// Read implements XPTY. | ||
func (p *UnixPty) Read(b []byte) (n int, err error) { | ||
return p.master.Read(b) | ||
} | ||
|
||
// Resize implements XPTY. | ||
func (p *UnixPty) Resize(width int, height int) (err error) { | ||
return p.setWinsize(width, height, 0, 0) | ||
} | ||
|
||
// SetWinsize sets window size for the PTY. | ||
func (p *UnixPty) SetWinsize(width, height, x, y int) error { | ||
return p.setWinsize(width, height, x, y) | ||
} | ||
|
||
// Size returns the size of the PTY. | ||
func (p *UnixPty) Size() (width, height int, err error) { | ||
return p.size() | ||
} | ||
|
||
// Start implements XPTY. | ||
func (p *UnixPty) Start(c *exec.Cmd) error { | ||
if c.Stdout == nil { | ||
c.Stdout = p.slave | ||
} | ||
if c.Stderr == nil { | ||
c.Stderr = p.slave | ||
} | ||
if c.Stdin == nil { | ||
c.Stdin = p.slave | ||
} | ||
if err := c.Start(); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// Write implements XPTY. | ||
func (p *UnixPty) Write(b []byte) (n int, err error) { | ||
return p.master.Write(b) | ||
} | ||
|
||
// Master returns the master end of the PTY. | ||
func (p *UnixPty) Master() *os.File { | ||
return p.master | ||
} | ||
|
||
// Slave returns the slave end of the PTY. | ||
func (p *UnixPty) Slave() *os.File { | ||
return p.slave | ||
} | ||
|
||
// Control runs the given function with the file descriptor of the master PTY. | ||
func (p *UnixPty) Control(fn func(fd uintptr)) error { | ||
conn, err := p.master.SyscallConn() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return conn.Control(fn) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris | ||
// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris | ||
|
||
package xpty | ||
|
||
func (p *UnixPty) setWinsize(int, int, int, int) error { | ||
return ErrUnsupported | ||
} | ||
|
||
func (*UnixPty) size() (int, int, error) { | ||
return 0, 0, ErrUnsupported | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris | ||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris | ||
|
||
package xpty | ||
|
||
import ( | ||
"github.com/charmbracelet/x/termios" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// setWinsize sets window size for the PTY. | ||
func (p *UnixPty) setWinsize(width, height, x, y int) error { | ||
var rErr error | ||
if err := p.Control(func(fd uintptr) { | ||
rErr = termios.SetWinsize(int(fd), &unix.Winsize{ | ||
Row: uint16(height), | ||
Col: uint16(width), | ||
Xpixel: uint16(x), | ||
Ypixel: uint16(y), | ||
}) | ||
}); err != nil { | ||
rErr = err | ||
} | ||
return rErr | ||
} | ||
|
||
// size returns the size of the PTY. | ||
func (p *UnixPty) size() (width, height int, err error) { | ||
var rErr error | ||
if err := p.Control(func(fd uintptr) { | ||
ws, err := termios.GetWinsize(int(fd)) | ||
if err != nil { | ||
rErr = err | ||
return | ||
} | ||
width = int(ws.Col) | ||
height = int(ws.Row) | ||
}); err != nil { | ||
rErr = err | ||
} | ||
|
||
return width, height, rErr | ||
} |
Oops, something went wrong.