mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 01:53:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			328 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			328 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// +build windows
 | 
						|
 | 
						|
package winterm
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"syscall"
 | 
						|
	"unsafe"
 | 
						|
)
 | 
						|
 | 
						|
//===========================================================================================================
 | 
						|
// IMPORTANT NOTE:
 | 
						|
//
 | 
						|
//	The methods below make extensive use of the "unsafe" package to obtain the required pointers.
 | 
						|
//	Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack
 | 
						|
//	variables) the pointers reference *before* the API completes.
 | 
						|
//
 | 
						|
//  As a result, in those cases, the code must hint that the variables remain in active by invoking the
 | 
						|
//	dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer
 | 
						|
//	require unsafe pointers.
 | 
						|
//
 | 
						|
//	If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform
 | 
						|
//	the garbage collector the variables remain in use if:
 | 
						|
//
 | 
						|
//	-- The value is not a pointer (e.g., int32, struct)
 | 
						|
//	-- The value is not referenced by the method after passing the pointer to Windows
 | 
						|
//
 | 
						|
//	See http://golang.org/doc/go1.3.
 | 
						|
//===========================================================================================================
 | 
						|
 | 
						|
var (
 | 
						|
	kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
 | 
						|
 | 
						|
	getConsoleCursorInfoProc       = kernel32DLL.NewProc("GetConsoleCursorInfo")
 | 
						|
	setConsoleCursorInfoProc       = kernel32DLL.NewProc("SetConsoleCursorInfo")
 | 
						|
	setConsoleCursorPositionProc   = kernel32DLL.NewProc("SetConsoleCursorPosition")
 | 
						|
	setConsoleModeProc             = kernel32DLL.NewProc("SetConsoleMode")
 | 
						|
	getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
 | 
						|
	setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize")
 | 
						|
	scrollConsoleScreenBufferProc  = kernel32DLL.NewProc("ScrollConsoleScreenBufferA")
 | 
						|
	setConsoleTextAttributeProc    = kernel32DLL.NewProc("SetConsoleTextAttribute")
 | 
						|
	setConsoleWindowInfoProc       = kernel32DLL.NewProc("SetConsoleWindowInfo")
 | 
						|
	writeConsoleOutputProc         = kernel32DLL.NewProc("WriteConsoleOutputW")
 | 
						|
	readConsoleInputProc           = kernel32DLL.NewProc("ReadConsoleInputW")
 | 
						|
	waitForSingleObjectProc        = kernel32DLL.NewProc("WaitForSingleObject")
 | 
						|
)
 | 
						|
 | 
						|
// Windows Console constants
 | 
						|
const (
 | 
						|
	// Console modes
 | 
						|
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
 | 
						|
	ENABLE_PROCESSED_INPUT        = 0x0001
 | 
						|
	ENABLE_LINE_INPUT             = 0x0002
 | 
						|
	ENABLE_ECHO_INPUT             = 0x0004
 | 
						|
	ENABLE_WINDOW_INPUT           = 0x0008
 | 
						|
	ENABLE_MOUSE_INPUT            = 0x0010
 | 
						|
	ENABLE_INSERT_MODE            = 0x0020
 | 
						|
	ENABLE_QUICK_EDIT_MODE        = 0x0040
 | 
						|
	ENABLE_EXTENDED_FLAGS         = 0x0080
 | 
						|
	ENABLE_AUTO_POSITION          = 0x0100
 | 
						|
	ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200
 | 
						|
 | 
						|
	ENABLE_PROCESSED_OUTPUT            = 0x0001
 | 
						|
	ENABLE_WRAP_AT_EOL_OUTPUT          = 0x0002
 | 
						|
	ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
 | 
						|
	DISABLE_NEWLINE_AUTO_RETURN        = 0x0008
 | 
						|
	ENABLE_LVB_GRID_WORLDWIDE          = 0x0010
 | 
						|
 | 
						|
	// Character attributes
 | 
						|
	// Note:
 | 
						|
	// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
 | 
						|
	//    Clearing all foreground or background colors results in black; setting all creates white.
 | 
						|
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
 | 
						|
	FOREGROUND_BLUE      uint16 = 0x0001
 | 
						|
	FOREGROUND_GREEN     uint16 = 0x0002
 | 
						|
	FOREGROUND_RED       uint16 = 0x0004
 | 
						|
	FOREGROUND_INTENSITY uint16 = 0x0008
 | 
						|
	FOREGROUND_MASK      uint16 = 0x000F
 | 
						|
 | 
						|
	BACKGROUND_BLUE      uint16 = 0x0010
 | 
						|
	BACKGROUND_GREEN     uint16 = 0x0020
 | 
						|
	BACKGROUND_RED       uint16 = 0x0040
 | 
						|
	BACKGROUND_INTENSITY uint16 = 0x0080
 | 
						|
	BACKGROUND_MASK      uint16 = 0x00F0
 | 
						|
 | 
						|
	COMMON_LVB_MASK          uint16 = 0xFF00
 | 
						|
	COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000
 | 
						|
	COMMON_LVB_UNDERSCORE    uint16 = 0x8000
 | 
						|
 | 
						|
	// Input event types
 | 
						|
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
 | 
						|
	KEY_EVENT                = 0x0001
 | 
						|
	MOUSE_EVENT              = 0x0002
 | 
						|
	WINDOW_BUFFER_SIZE_EVENT = 0x0004
 | 
						|
	MENU_EVENT               = 0x0008
 | 
						|
	FOCUS_EVENT              = 0x0010
 | 
						|
 | 
						|
	// WaitForSingleObject return codes
 | 
						|
	WAIT_ABANDONED = 0x00000080
 | 
						|
	WAIT_FAILED    = 0xFFFFFFFF
 | 
						|
	WAIT_SIGNALED  = 0x0000000
 | 
						|
	WAIT_TIMEOUT   = 0x00000102
 | 
						|
 | 
						|
	// WaitForSingleObject wait duration
 | 
						|
	WAIT_INFINITE       = 0xFFFFFFFF
 | 
						|
	WAIT_ONE_SECOND     = 1000
 | 
						|
	WAIT_HALF_SECOND    = 500
 | 
						|
	WAIT_QUARTER_SECOND = 250
 | 
						|
)
 | 
						|
 | 
						|
// Windows API Console types
 | 
						|
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD)
 | 
						|
// -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment
 | 
						|
type (
 | 
						|
	CHAR_INFO struct {
 | 
						|
		UnicodeChar uint16
 | 
						|
		Attributes  uint16
 | 
						|
	}
 | 
						|
 | 
						|
	CONSOLE_CURSOR_INFO struct {
 | 
						|
		Size    uint32
 | 
						|
		Visible int32
 | 
						|
	}
 | 
						|
 | 
						|
	CONSOLE_SCREEN_BUFFER_INFO struct {
 | 
						|
		Size              COORD
 | 
						|
		CursorPosition    COORD
 | 
						|
		Attributes        uint16
 | 
						|
		Window            SMALL_RECT
 | 
						|
		MaximumWindowSize COORD
 | 
						|
	}
 | 
						|
 | 
						|
	COORD struct {
 | 
						|
		X int16
 | 
						|
		Y int16
 | 
						|
	}
 | 
						|
 | 
						|
	SMALL_RECT struct {
 | 
						|
		Left   int16
 | 
						|
		Top    int16
 | 
						|
		Right  int16
 | 
						|
		Bottom int16
 | 
						|
	}
 | 
						|
 | 
						|
	// INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest
 | 
						|
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
 | 
						|
	INPUT_RECORD struct {
 | 
						|
		EventType uint16
 | 
						|
		KeyEvent  KEY_EVENT_RECORD
 | 
						|
	}
 | 
						|
 | 
						|
	KEY_EVENT_RECORD struct {
 | 
						|
		KeyDown         int32
 | 
						|
		RepeatCount     uint16
 | 
						|
		VirtualKeyCode  uint16
 | 
						|
		VirtualScanCode uint16
 | 
						|
		UnicodeChar     uint16
 | 
						|
		ControlKeyState uint32
 | 
						|
	}
 | 
						|
 | 
						|
	WINDOW_BUFFER_SIZE struct {
 | 
						|
		Size COORD
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
// boolToBOOL converts a Go bool into a Windows int32.
 | 
						|
func boolToBOOL(f bool) int32 {
 | 
						|
	if f {
 | 
						|
		return int32(1)
 | 
						|
	} else {
 | 
						|
		return int32(0)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor.
 | 
						|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx.
 | 
						|
func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
 | 
						|
	r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// SetConsoleCursorInfo sets the size and visiblity of the console cursor.
 | 
						|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx.
 | 
						|
func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
 | 
						|
	r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// SetConsoleCursorPosition location of the console cursor.
 | 
						|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx.
 | 
						|
func SetConsoleCursorPosition(handle uintptr, coord COORD) error {
 | 
						|
	r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord))
 | 
						|
	use(coord)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// GetConsoleMode gets the console mode for given file descriptor
 | 
						|
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx.
 | 
						|
func GetConsoleMode(handle uintptr) (mode uint32, err error) {
 | 
						|
	err = syscall.GetConsoleMode(syscall.Handle(handle), &mode)
 | 
						|
	return mode, err
 | 
						|
}
 | 
						|
 | 
						|
// SetConsoleMode sets the console mode for given file descriptor
 | 
						|
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
 | 
						|
func SetConsoleMode(handle uintptr, mode uint32) error {
 | 
						|
	r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0)
 | 
						|
	use(mode)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
 | 
						|
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx.
 | 
						|
func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
 | 
						|
	info := CONSOLE_SCREEN_BUFFER_INFO{}
 | 
						|
	err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0))
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &info, nil
 | 
						|
}
 | 
						|
 | 
						|
func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error {
 | 
						|
	r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char)))
 | 
						|
	use(scrollRect)
 | 
						|
	use(clipRect)
 | 
						|
	use(destOrigin)
 | 
						|
	use(char)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// SetConsoleScreenBufferSize sets the size of the console screen buffer.
 | 
						|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx.
 | 
						|
func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
 | 
						|
	r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord))
 | 
						|
	use(coord)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// SetConsoleTextAttribute sets the attributes of characters written to the
 | 
						|
// console screen buffer by the WriteFile or WriteConsole function.
 | 
						|
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
 | 
						|
func SetConsoleTextAttribute(handle uintptr, attribute uint16) error {
 | 
						|
	r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)
 | 
						|
	use(attribute)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// SetConsoleWindowInfo sets the size and position of the console screen buffer's window.
 | 
						|
// Note that the size and location must be within and no larger than the backing console screen buffer.
 | 
						|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx.
 | 
						|
func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error {
 | 
						|
	r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect)))
 | 
						|
	use(isAbsolute)
 | 
						|
	use(rect)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer.
 | 
						|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx.
 | 
						|
func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error {
 | 
						|
	r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion)))
 | 
						|
	use(buffer)
 | 
						|
	use(bufferSize)
 | 
						|
	use(bufferCoord)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// ReadConsoleInput reads (and removes) data from the console input buffer.
 | 
						|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx.
 | 
						|
func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error {
 | 
						|
	r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count)))
 | 
						|
	use(buffer)
 | 
						|
	return checkError(r1, r2, err)
 | 
						|
}
 | 
						|
 | 
						|
// WaitForSingleObject waits for the passed handle to be signaled.
 | 
						|
// It returns true if the handle was signaled; false otherwise.
 | 
						|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx.
 | 
						|
func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) {
 | 
						|
	r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait)))
 | 
						|
	switch r1 {
 | 
						|
	case WAIT_ABANDONED, WAIT_TIMEOUT:
 | 
						|
		return false, nil
 | 
						|
	case WAIT_SIGNALED:
 | 
						|
		return true, nil
 | 
						|
	}
 | 
						|
	use(msWait)
 | 
						|
	return false, err
 | 
						|
}
 | 
						|
 | 
						|
// String helpers
 | 
						|
func (info CONSOLE_SCREEN_BUFFER_INFO) String() string {
 | 
						|
	return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize)
 | 
						|
}
 | 
						|
 | 
						|
func (coord COORD) String() string {
 | 
						|
	return fmt.Sprintf("%v,%v", coord.X, coord.Y)
 | 
						|
}
 | 
						|
 | 
						|
func (rect SMALL_RECT) String() string {
 | 
						|
	return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom)
 | 
						|
}
 | 
						|
 | 
						|
// checkError evaluates the results of a Windows API call and returns the error if it failed.
 | 
						|
func checkError(r1, r2 uintptr, err error) error {
 | 
						|
	// Windows APIs return non-zero to indicate success
 | 
						|
	if r1 != 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Return the error if provided, otherwise default to EINVAL
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return syscall.EINVAL
 | 
						|
}
 | 
						|
 | 
						|
// coordToPointer converts a COORD into a uintptr (by fooling the type system).
 | 
						|
func coordToPointer(c COORD) uintptr {
 | 
						|
	// Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass.
 | 
						|
	return uintptr(*((*uint32)(unsafe.Pointer(&c))))
 | 
						|
}
 | 
						|
 | 
						|
// use is a no-op, but the compiler cannot see that it is.
 | 
						|
// Calling use(p) ensures that p is kept live until that point.
 | 
						|
func use(p interface{}) {}
 |