Refactor pkg/term package for Windows tty support

Signed-off-by: John Gossman <johngos@microsoft.com>
This commit is contained in:
John Gossman 2014-10-23 16:44:57 -07:00 committed by Ahmet Alp Balkan
parent d246e5f28e
commit 2d644f8ab1
3 changed files with 178 additions and 0 deletions

87
term/console_windows.go Normal file
View 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
}

View file

@ -1,3 +1,5 @@
// +build !windows
package term
import (

89
term/term_windows.go Normal file
View 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
}