Explorar o código

Refactor pkg/term package for Windows tty support

Signed-off-by: John Gossman <johngos@microsoft.com>
John Gossman %!s(int64=10) %!d(string=hai) anos
pai
achega
975b6e598d
Modificáronse 3 ficheiros con 178 adicións e 0 borrados
  1. 87 0
      pkg/term/console_windows.go
  2. 2 0
      pkg/term/term.go
  3. 89 0
      pkg/term/term_windows.go

+ 87 - 0
pkg/term/console_windows.go

@@ -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
+}

+ 2 - 0
pkg/term/term.go

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

+ 89 - 0
pkg/term/term_windows.go

@@ -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
+}