Skip to content

Commit a7ba593

Browse files
committed
Fix reading from and writing to console on windows
The windows implementation now follows the unix implementation more closely: . the actual `os.File` passed is now used to create the console. . `Read` and `Write` are now implemented by forwarding to the underlying `os.File`
1 parent 6fe6f36 commit a7ba593

File tree

1 file changed

+36
-72
lines changed

1 file changed

+36
-72
lines changed

console_windows.go

+36-72
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package console
1818

1919
import (
20-
"fmt"
2120
"os"
2221

2322
"github.com/pkg/errors"
@@ -29,90 +28,55 @@ var (
2928
ErrNotImplemented = errors.New("not implemented")
3029
)
3130

32-
func (m *master) initStdios() {
33-
m.in = windows.Handle(os.Stdin.Fd())
34-
if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil {
35-
// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
36-
if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
37-
vtInputSupported = true
38-
}
39-
// Unconditionally set the console mode back even on failure because SetConsoleMode
40-
// remembers invalid bits on input handles.
41-
windows.SetConsoleMode(m.in, m.inMode)
42-
} else {
43-
fmt.Printf("failed to get console mode for stdin: %v\n", err)
44-
}
45-
46-
m.out = windows.Handle(os.Stdout.Fd())
47-
if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil {
48-
if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
49-
m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
31+
func (m *master) init() {
32+
m.h = windows.Handle(m.f.Fd())
33+
if err := windows.GetConsoleMode(m.h, &m.mode); err == nil {
34+
if m.f == os.Stdin {
35+
// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
36+
if err = windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
37+
vtInputSupported = true
38+
}
39+
// Unconditionally set the console mode back even on failure because SetConsoleMode
40+
// remembers invalid bits on input handles.
41+
windows.SetConsoleMode(m.h, m.mode)
42+
} else if err := windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
43+
m.mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
5044
} else {
51-
windows.SetConsoleMode(m.out, m.outMode)
45+
windows.SetConsoleMode(m.h, m.mode)
5246
}
53-
} else {
54-
fmt.Printf("failed to get console mode for stdout: %v\n", err)
55-
}
56-
57-
m.err = windows.Handle(os.Stderr.Fd())
58-
if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil {
59-
if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
60-
m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
61-
} else {
62-
windows.SetConsoleMode(m.err, m.errMode)
63-
}
64-
} else {
65-
fmt.Printf("failed to get console mode for stderr: %v\n", err)
6647
}
6748
}
6849

6950
type master struct {
70-
in windows.Handle
71-
inMode uint32
72-
73-
out windows.Handle
74-
outMode uint32
75-
76-
err windows.Handle
77-
errMode uint32
51+
h windows.Handle
52+
mode uint32
53+
f *os.File
7854
}
7955

8056
func (m *master) SetRaw() error {
81-
if err := makeInputRaw(m.in, m.inMode); err != nil {
82-
return err
57+
if m.f == os.Stdin {
58+
if err := makeInputRaw(m.h, m.mode); err != nil {
59+
return err
60+
}
61+
} else {
62+
// Set StdOut and StdErr to raw mode, we ignore failures since
63+
// windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
64+
// Windows.
65+
windows.SetConsoleMode(m.h, m.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
8366
}
84-
85-
// Set StdOut and StdErr to raw mode, we ignore failures since
86-
// windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
87-
// Windows.
88-
89-
windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
90-
91-
windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
92-
9367
return nil
9468
}
9569

9670
func (m *master) Reset() error {
97-
for _, s := range []struct {
98-
fd windows.Handle
99-
mode uint32
100-
}{
101-
{m.in, m.inMode},
102-
{m.out, m.outMode},
103-
{m.err, m.errMode},
104-
} {
105-
if err := windows.SetConsoleMode(s.fd, s.mode); err != nil {
106-
return errors.Wrap(err, "unable to restore console mode")
107-
}
71+
if err := windows.SetConsoleMode(m.h, m.mode); err != nil {
72+
return errors.Wrap(err, "unable to restore console mode")
10873
}
109-
11074
return nil
11175
}
11276

11377
func (m *master) Size() (WinSize, error) {
11478
var info windows.ConsoleScreenBufferInfo
115-
err := windows.GetConsoleScreenBufferInfo(m.out, &info)
79+
err := windows.GetConsoleScreenBufferInfo(m.h, &info)
11680
if err != nil {
11781
return WinSize{}, errors.Wrap(err, "unable to get console info")
11882
}
@@ -134,11 +98,11 @@ func (m *master) ResizeFrom(c Console) error {
13498
}
13599

136100
func (m *master) DisableEcho() error {
137-
mode := m.inMode &^ windows.ENABLE_ECHO_INPUT
101+
mode := m.mode &^ windows.ENABLE_ECHO_INPUT
138102
mode |= windows.ENABLE_PROCESSED_INPUT
139103
mode |= windows.ENABLE_LINE_INPUT
140104

141-
if err := windows.SetConsoleMode(m.in, mode); err != nil {
105+
if err := windows.SetConsoleMode(m.h, mode); err != nil {
142106
return errors.Wrap(err, "unable to set console to disable echo")
143107
}
144108

@@ -150,15 +114,15 @@ func (m *master) Close() error {
150114
}
151115

152116
func (m *master) Read(b []byte) (int, error) {
153-
panic("not implemented on windows")
117+
return m.f.Read(b)
154118
}
155119

156120
func (m *master) Write(b []byte) (int, error) {
157-
panic("not implemented on windows")
121+
return m.f.Write(b)
158122
}
159123

160124
func (m *master) Fd() uintptr {
161-
return uintptr(m.in)
125+
return uintptr(m.h)
162126
}
163127

164128
// on windows, console can only be made from os.Std{in,out,err}, hence there
@@ -210,7 +174,7 @@ func newMaster(f *os.File) (Console, error) {
210174
if f != os.Stdin && f != os.Stdout && f != os.Stderr {
211175
return nil, errors.New("creating a console from a file is not supported on windows")
212176
}
213-
m := &master{}
214-
m.initStdios()
177+
m := &master{f: f}
178+
m.init()
215179
return m, nil
216180
}

0 commit comments

Comments
 (0)