Browse code

Refactor pkg/term package for Windows tty support

Signed-off-by: John Gossman <johngos@microsoft.com>

John Gossman authored on 2014/10/24 08:44:57
Showing 3 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,87 @@
0
+// +build windows
1
+
2
+package term
3
+
4
+import (
5
+	"syscall"
6
+	"unsafe"
7
+)
8
+
9
+const (
10
+	// Consts for Get/SetConsoleMode function
11
+	// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
12
+	ENABLE_ECHO_INPUT      = 0x0004
13
+	ENABLE_INSERT_MODE     = 0x0020
14
+	ENABLE_LINE_INPUT      = 0x0002
15
+	ENABLE_MOUSE_INPUT     = 0x0010
16
+	ENABLE_PROCESSED_INPUT = 0x0001
17
+	ENABLE_QUICK_EDIT_MODE = 0x0040
18
+	ENABLE_WINDOW_INPUT    = 0x0008
19
+	// If parameter is a screen buffer handle, additional values
20
+	ENABLE_PROCESSED_OUTPUT   = 0x0001
21
+	ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
22
+)
23
+
24
+var kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
25
+
26
+var (
27
+	setConsoleModeProc             = kernel32DLL.NewProc("SetConsoleMode")
28
+	getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
29
+)
30
+
31
+func GetConsoleMode(fileDesc uintptr) (uint32, error) {
32
+	var mode uint32
33
+	err := syscall.GetConsoleMode(syscall.Handle(fileDesc), &mode)
34
+	return mode, err
35
+}
36
+
37
+func SetConsoleMode(fileDesc uintptr, mode uint32) error {
38
+	r, _, err := setConsoleModeProc.Call(fileDesc, uintptr(mode), 0)
39
+	if r == 0 {
40
+		if err != nil {
41
+			return err
42
+		}
43
+		return syscall.EINVAL
44
+	}
45
+	return nil
46
+}
47
+
48
+// types for calling GetConsoleScreenBufferInfo
49
+// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
50
+type (
51
+	SHORT int16
52
+
53
+	SMALL_RECT struct {
54
+		Left   SHORT
55
+		Top    SHORT
56
+		Right  SHORT
57
+		Bottom SHORT
58
+	}
59
+
60
+	COORD struct {
61
+		X SHORT
62
+		Y SHORT
63
+	}
64
+
65
+	WORD uint16
66
+
67
+	CONSOLE_SCREEN_BUFFER_INFO struct {
68
+		dwSize              COORD
69
+		dwCursorPosition    COORD
70
+		wAttributes         WORD
71
+		srWindow            SMALL_RECT
72
+		dwMaximumWindowSize COORD
73
+	}
74
+)
75
+
76
+func GetConsoleScreenBufferInfo(fileDesc uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
77
+	var info CONSOLE_SCREEN_BUFFER_INFO
78
+	r, _, err := getConsoleScreenBufferInfoProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&info)), 0)
79
+	if r == 0 {
80
+		if err != nil {
81
+			return nil, err
82
+		}
83
+		return nil, syscall.EINVAL
84
+	}
85
+	return &info, nil
86
+}
... ...
@@ -1,3 +1,5 @@
1
+// +build !windows
2
+
1 3
 package term
2 4
 
3 5
 import (
4 6
new file mode 100644
... ...
@@ -0,0 +1,89 @@
0
+// +build windows
1
+
2
+package term
3
+
4
+type State struct {
5
+	mode uint32
6
+}
7
+
8
+type Winsize struct {
9
+	Height uint16
10
+	Width  uint16
11
+	x      uint16
12
+	y      uint16
13
+}
14
+
15
+func GetWinsize(fd uintptr) (*Winsize, error) {
16
+	ws := &Winsize{}
17
+	var info *CONSOLE_SCREEN_BUFFER_INFO
18
+	info, err := GetConsoleScreenBufferInfo(fd)
19
+	if err != nil {
20
+		return nil, err
21
+	}
22
+	ws.Height = uint16(info.srWindow.Right - info.srWindow.Left + 1)
23
+	ws.Width = uint16(info.srWindow.Bottom - info.srWindow.Top + 1)
24
+
25
+	ws.x = 0 // todo azlinux -- this is the pixel size of the Window, and not currently used by any caller
26
+	ws.y = 0
27
+
28
+	return ws, nil
29
+}
30
+
31
+func SetWinsize(fd uintptr, ws *Winsize) error {
32
+	return nil
33
+}
34
+
35
+// IsTerminal returns true if the given file descriptor is a terminal.
36
+func IsTerminal(fd uintptr) bool {
37
+	_, e := GetConsoleMode(fd)
38
+	return e == nil
39
+}
40
+
41
+// Restore restores the terminal connected to the given file descriptor to a
42
+// previous state.
43
+func RestoreTerminal(fd uintptr, state *State) error {
44
+	return SetConsoleMode(fd, state.mode)
45
+}
46
+
47
+func SaveState(fd uintptr) (*State, error) {
48
+	mode, e := GetConsoleMode(fd)
49
+	if e != nil {
50
+		return nil, e
51
+	}
52
+	return &State{mode}, nil
53
+}
54
+
55
+// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
56
+func DisableEcho(fd uintptr, state *State) error {
57
+	state.mode &^= (ENABLE_ECHO_INPUT)
58
+	state.mode |= (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
59
+	return SetConsoleMode(fd, state.mode)
60
+}
61
+
62
+func SetRawTerminal(fd uintptr) (*State, error) {
63
+	oldState, err := MakeRaw(fd)
64
+	if err != nil {
65
+		return nil, err
66
+	}
67
+	// TODO (azlinux): implement handling interrupt and restore state of terminal
68
+	return oldState, err
69
+}
70
+
71
+// MakeRaw puts the terminal connected to the given file descriptor into raw
72
+// mode and returns the previous state of the terminal so that it can be
73
+// restored.
74
+func MakeRaw(fd uintptr) (*State, error) {
75
+	var state *State
76
+	state, err := SaveState(fd)
77
+	if err != nil {
78
+		return nil, err
79
+	}
80
+
81
+	// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
82
+	state.mode &^= (ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
83
+	err = SetConsoleMode(fd, state.mode)
84
+	if err != nil {
85
+		return nil, err
86
+	}
87
+	return state, nil
88
+}