File: xpty.go

package info (click to toggle)
golang-github-charmbracelet-x 0.0~git20240809.9ab0ca0%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,004 kB
  • sloc: sh: 55; makefile: 5
file content (100 lines) | stat: -rw-r--r-- 2,263 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package xpty

import (
	"context"
	"errors"
	"io"
	"os"
	"os/exec"
	"runtime"

	"github.com/charmbracelet/x/term"
	"github.com/creack/pty"
)

// ErrUnsupported is returned when a feature is not supported.
var ErrUnsupported = pty.ErrUnsupported

// Pty represents a PTY (pseudo-terminal) interface.
type Pty interface {
	term.File
	io.ReadWriteCloser

	// Resize resizes the PTY.
	Resize(width, height int) error

	// Size returns the size of the PTY.
	Size() (width, height int, err error)

	// Name returns the name of the PTY.
	Name() string

	// Start starts a command on the PTY.
	// The command started will have its standard input, output, and error
	// connected to the PTY.
	// On Windows, calling Wait won't work since the Go runtime doesn't handle
	// ConPTY processes correctly. See https://github.com/golang/go/pull/62710.
	Start(cmd *exec.Cmd) error
}

// Options represents PTY options.
type Options struct {
	Flags int
}

// PtyOption is a PTY option.
type PtyOption func(o Options)

// NewPty creates a new PTY.
//
// The returned PTY will be a Unix PTY on Unix systems and a ConPTY on Windows.
// The width and height parameters specify the initial size of the PTY.
// You can pass additional options to the PTY by passing PtyOptions.
//
//	pty, err := xpty.NewPty(80, 24)
//	if err != nil {
//	   // handle error
//	}
//
//	defer pty.Close() // Make sure to close the PTY when done.
//	switch pty := pty.(type) {
//	case xpty.UnixPty:
//	    // Unix PTY
//	case xpty.ConPty:
//	    // ConPTY
//	}
func NewPty(width, height int, opts ...PtyOption) (Pty, error) {
	if runtime.GOOS == "windows" {
		return NewConPty(width, height, opts...)
	}
	return NewUnixPty(width, height, opts...)
}

// WaitProcess waits for the process to exit.
// This exists because on Windows, cmd.Wait() doesn't work with ConPty.
func WaitProcess(ctx context.Context, c *exec.Cmd) (err error) {
	if c.Process == nil {
		return errors.New("process not started")
	}

	type result struct {
		*os.ProcessState
		error
	}

	donec := make(chan result, 1)
	go func() {
		state, err := c.Process.Wait()
		donec <- result{state, err}
	}()

	select {
	case <-ctx.Done():
		err = c.Process.Kill()
	case r := <-donec:
		c.ProcessState = r.ProcessState
		err = r.error
	}

	return
}