返回 DeepSeek-Reasonix
terminal_process_unix.go
根目录 / desktop / terminal_process_unix.go
1 //go:build !windows
2
3 package main
4
5 import (
6 "errors"
7 "os"
8 "os/exec"
9 "sync"
10 "syscall"
11
12 "github.com/creack/pty"
13 )
14
15 type unixTerminalProcess struct {
16 cmd *exec.Cmd
17 pty *os.File
18 closeOnce sync.Once
19 cleanup func()
20 }
21
22 func terminalPlatformAvailable() (bool, string) {
23 return true, ""
24 }
25
26 func startTerminalProcess(spec terminalStartSpec) (terminalProcess, error) {
27 env, cleanup, err := terminalInputEnvironment(spec)
28 if err != nil {
29 return nil, err
30 }
31 cmd := exec.Command(spec.command.path, spec.command.args...)
32 cmd.Dir = spec.dir
33 cmd.Env = env
34 file, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: uint16(spec.rows), Cols: uint16(spec.cols)})
35 if err != nil {
36 cleanup()
37 return nil, err
38 }
39 return &unixTerminalProcess{cmd: cmd, pty: file, cleanup: cleanup}, nil
40 }
41
42 func (p *unixTerminalProcess) Read(data []byte) (int, error) {
43 return p.pty.Read(data)
44 }
45
46 func (p *unixTerminalProcess) Write(data []byte) (int, error) {
47 return p.pty.Write(data)
48 }
49
50 func (p *unixTerminalProcess) Resize(cols, rows int) error {
51 return pty.Setsize(p.pty, &pty.Winsize{Rows: uint16(rows), Cols: uint16(cols)})
52 }
53
54 func (p *unixTerminalProcess) Wait() (int, error) {
55 defer p.cleanup()
56 err := p.cmd.Wait()
57 if p.cmd.ProcessState != nil {
58 return p.cmd.ProcessState.ExitCode(), err
59 }
60 var exitErr *exec.ExitError
61 if errors.As(err, &exitErr) {
62 return exitErr.ExitCode(), err
63 }
64 return -1, err
65 }
66
67 func (p *unixTerminalProcess) Close() error {
68 defer p.cleanup()
69 var closeErr error
70 p.closeOnce.Do(func() {
71 if p.cmd.Process != nil {
72 // creack/pty starts the command in a new session. Killing the process
73 // group prevents foreground children from surviving a closed tab.
74 _ = syscall.Kill(-p.cmd.Process.Pid, syscall.SIGKILL)
75 }
76 closeErr = p.pty.Close()
77 })
78 return closeErr
79 }
80
80 lines GO