Refactor pkg/term package for Windows tty support
Signed-off-by: John Gossman <johngos@microsoft.com>
This commit is contained in:
parent
d246e5f28e
commit
2d644f8ab1
3 changed files with 178 additions and 0 deletions
87
term/console_windows.go
Normal file
87
term/console_windows.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
// +build windows
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Consts for Get/SetConsoleMode function
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
|
||||
ENABLE_ECHO_INPUT = 0x0004
|
||||
ENABLE_INSERT_MODE = 0x0020
|
||||
ENABLE_LINE_INPUT = 0x0002
|
||||
ENABLE_MOUSE_INPUT = 0x0010
|
||||
ENABLE_PROCESSED_INPUT = 0x0001
|
||||
ENABLE_QUICK_EDIT_MODE = 0x0040
|
||||
ENABLE_WINDOW_INPUT = 0x0008
|
||||
// If parameter is a screen buffer handle, additional values
|
||||
ENABLE_PROCESSED_OUTPUT = 0x0001
|
||||
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
|
||||
)
|
||||
|
||||
var kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode")
|
||||
getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
|
||||
)
|
||||
|
||||
func GetConsoleMode(fileDesc uintptr) (uint32, error) {
|
||||
var mode uint32
|
||||
err := syscall.GetConsoleMode(syscall.Handle(fileDesc), &mode)
|
||||
return mode, err
|
||||
}
|
||||
|
||||
func SetConsoleMode(fileDesc uintptr, mode uint32) error {
|
||||
r, _, err := setConsoleModeProc.Call(fileDesc, uintptr(mode), 0)
|
||||
if r == 0 {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// types for calling GetConsoleScreenBufferInfo
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
|
||||
type (
|
||||
SHORT int16
|
||||
|
||||
SMALL_RECT struct {
|
||||
Left SHORT
|
||||
Top SHORT
|
||||
Right SHORT
|
||||
Bottom SHORT
|
||||
}
|
||||
|
||||
COORD struct {
|
||||
X SHORT
|
||||
Y SHORT
|
||||
}
|
||||
|
||||
WORD uint16
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO struct {
|
||||
dwSize COORD
|
||||
dwCursorPosition COORD
|
||||
wAttributes WORD
|
||||
srWindow SMALL_RECT
|
||||
dwMaximumWindowSize COORD
|
||||
}
|
||||
)
|
||||
|
||||
func GetConsoleScreenBufferInfo(fileDesc uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
|
||||
var info CONSOLE_SCREEN_BUFFER_INFO
|
||||
r, _, err := getConsoleScreenBufferInfoProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&info)), 0)
|
||||
if r == 0 {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
return &info, nil
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
// +build !windows
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
|
|
89
term/term_windows.go
Normal file
89
term/term_windows.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
// +build windows
|
||||
|
||||
package term
|
||||
|
||||
type State struct {
|
||||
mode uint32
|
||||
}
|
||||
|
||||
type Winsize struct {
|
||||
Height uint16
|
||||
Width uint16
|
||||
x uint16
|
||||
y uint16
|
||||
}
|
||||
|
||||
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||
ws := &Winsize{}
|
||||
var info *CONSOLE_SCREEN_BUFFER_INFO
|
||||
info, err := GetConsoleScreenBufferInfo(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ws.Height = uint16(info.srWindow.Right - info.srWindow.Left + 1)
|
||||
ws.Width = uint16(info.srWindow.Bottom - info.srWindow.Top + 1)
|
||||
|
||||
ws.x = 0 // todo azlinux -- this is the pixel size of the Window, and not currently used by any caller
|
||||
ws.y = 0
|
||||
|
||||
return ws, nil
|
||||
}
|
||||
|
||||
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, e := GetConsoleMode(fd)
|
||||
return e == nil
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func RestoreTerminal(fd uintptr, state *State) error {
|
||||
return SetConsoleMode(fd, state.mode)
|
||||
}
|
||||
|
||||
func SaveState(fd uintptr) (*State, error) {
|
||||
mode, e := GetConsoleMode(fd)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return &State{mode}, nil
|
||||
}
|
||||
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
|
||||
func DisableEcho(fd uintptr, state *State) error {
|
||||
state.mode &^= (ENABLE_ECHO_INPUT)
|
||||
state.mode |= (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
|
||||
return SetConsoleMode(fd, state.mode)
|
||||
}
|
||||
|
||||
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||
oldState, err := MakeRaw(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO (azlinux): implement handling interrupt and restore state of terminal
|
||||
return oldState, err
|
||||
}
|
||||
|
||||
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd uintptr) (*State, error) {
|
||||
var state *State
|
||||
state, err := SaveState(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
|
||||
state.mode &^= (ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
|
||||
err = SetConsoleMode(fd, state.mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return state, nil
|
||||
}
|
Loading…
Reference in a new issue