1 package term
2
3 import (
4 "fmt"
5 "io"
6 "os"
7 "os/signal"
8
9 windowsconsole "github.com/moby/term/windows"
10 "golang.org/x/sys/windows"
11 )
12
13
14 type terminalState struct {
15 mode uint32
16 }
17
18
19 var vtInputSupported bool
20
21 func stdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
22
23
24 var (
25 emulateStdin, emulateStdout, emulateStderr bool
26
27 mode uint32
28 )
29
30 fd := windows.Handle(os.Stdin.Fd())
31 if err := windows.GetConsoleMode(fd, &mode); err == nil {
32
33 if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
34 emulateStdin = true
35 } else {
36 vtInputSupported = true
37 }
38
39
40 _ = windows.SetConsoleMode(fd, mode)
41 }
42
43 fd = windows.Handle(os.Stdout.Fd())
44 if err := windows.GetConsoleMode(fd, &mode); err == nil {
45
46 if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
47 emulateStdout = true
48 } else {
49 _ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
50 }
51 }
52
53 fd = windows.Handle(os.Stderr.Fd())
54 if err := windows.GetConsoleMode(fd, &mode); err == nil {
55
56 if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
57 emulateStderr = true
58 } else {
59 _ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
60 }
61 }
62
63 if emulateStdin {
64 h := uint32(windows.STD_INPUT_HANDLE)
65 stdIn = windowsconsole.NewAnsiReader(int(h))
66 } else {
67 stdIn = os.Stdin
68 }
69
70 if emulateStdout {
71 h := uint32(windows.STD_OUTPUT_HANDLE)
72 stdOut = windowsconsole.NewAnsiWriter(int(h))
73 } else {
74 stdOut = os.Stdout
75 }
76
77 if emulateStderr {
78 h := uint32(windows.STD_ERROR_HANDLE)
79 stdErr = windowsconsole.NewAnsiWriter(int(h))
80 } else {
81 stdErr = os.Stderr
82 }
83
84 return stdIn, stdOut, stdErr
85 }
86
87 func getFdInfo(in interface{}) (uintptr, bool) {
88 return windowsconsole.GetHandleInfo(in)
89 }
90
91 func getWinsize(fd uintptr) (*Winsize, error) {
92 var info windows.ConsoleScreenBufferInfo
93 if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
94 return nil, err
95 }
96
97 winsize := &Winsize{
98 Width: uint16(info.Window.Right - info.Window.Left + 1),
99 Height: uint16(info.Window.Bottom - info.Window.Top + 1),
100 }
101
102 return winsize, nil
103 }
104
105 func setWinsize(fd uintptr, ws *Winsize) error {
106 return fmt.Errorf("not implemented on Windows")
107 }
108
109 func isTerminal(fd uintptr) bool {
110 var mode uint32
111 err := windows.GetConsoleMode(windows.Handle(fd), &mode)
112 return err == nil
113 }
114
115 func restoreTerminal(fd uintptr, state *State) error {
116 return windows.SetConsoleMode(windows.Handle(fd), state.mode)
117 }
118
119 func saveState(fd uintptr) (*State, error) {
120 var mode uint32
121
122 if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil {
123 return nil, err
124 }
125
126 return &State{mode: mode}, nil
127 }
128
129 func disableEcho(fd uintptr, state *State) error {
130
131 mode := state.mode
132 mode &^= windows.ENABLE_ECHO_INPUT
133 mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT
134 err := windows.SetConsoleMode(windows.Handle(fd), mode)
135 if err != nil {
136 return err
137 }
138
139
140 restoreAtInterrupt(fd, state)
141 return nil
142 }
143
144 func setRawTerminal(fd uintptr) (*State, error) {
145 oldState, err := MakeRaw(fd)
146 if err != nil {
147 return nil, err
148 }
149
150
151 restoreAtInterrupt(fd, oldState)
152 return oldState, err
153 }
154
155 func setRawTerminalOutput(fd uintptr) (*State, error) {
156 oldState, err := saveState(fd)
157 if err != nil {
158 return nil, err
159 }
160
161
162
163 _ = windows.SetConsoleMode(windows.Handle(fd), oldState.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
164 return oldState, err
165 }
166
167 func restoreAtInterrupt(fd uintptr, state *State) {
168 sigchan := make(chan os.Signal, 1)
169 signal.Notify(sigchan, os.Interrupt)
170
171 go func() {
172 _ = <-sigchan
173 _ = RestoreTerminal(fd, state)
174 os.Exit(0)
175 }()
176 }
177
View as plain text