...

Source file src/github.com/Azure/go-ansiterm/winterm/api.go

Documentation: github.com/Azure/go-ansiterm/winterm

     1  // +build windows
     2  
     3  package winterm
     4  
     5  import (
     6  	"fmt"
     7  	"syscall"
     8  	"unsafe"
     9  )
    10  
    11  //===========================================================================================================
    12  // IMPORTANT NOTE:
    13  //
    14  //	The methods below make extensive use of the "unsafe" package to obtain the required pointers.
    15  //	Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack
    16  //	variables) the pointers reference *before* the API completes.
    17  //
    18  //  As a result, in those cases, the code must hint that the variables remain in active by invoking the
    19  //	dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer
    20  //	require unsafe pointers.
    21  //
    22  //	If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform
    23  //	the garbage collector the variables remain in use if:
    24  //
    25  //	-- The value is not a pointer (e.g., int32, struct)
    26  //	-- The value is not referenced by the method after passing the pointer to Windows
    27  //
    28  //	See http://golang.org/doc/go1.3.
    29  //===========================================================================================================
    30  
    31  var (
    32  	kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
    33  
    34  	getConsoleCursorInfoProc       = kernel32DLL.NewProc("GetConsoleCursorInfo")
    35  	setConsoleCursorInfoProc       = kernel32DLL.NewProc("SetConsoleCursorInfo")
    36  	setConsoleCursorPositionProc   = kernel32DLL.NewProc("SetConsoleCursorPosition")
    37  	setConsoleModeProc             = kernel32DLL.NewProc("SetConsoleMode")
    38  	getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
    39  	setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize")
    40  	scrollConsoleScreenBufferProc  = kernel32DLL.NewProc("ScrollConsoleScreenBufferA")
    41  	setConsoleTextAttributeProc    = kernel32DLL.NewProc("SetConsoleTextAttribute")
    42  	setConsoleWindowInfoProc       = kernel32DLL.NewProc("SetConsoleWindowInfo")
    43  	writeConsoleOutputProc         = kernel32DLL.NewProc("WriteConsoleOutputW")
    44  	readConsoleInputProc           = kernel32DLL.NewProc("ReadConsoleInputW")
    45  	waitForSingleObjectProc        = kernel32DLL.NewProc("WaitForSingleObject")
    46  )
    47  
    48  // Windows Console constants
    49  const (
    50  	// Console modes
    51  	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
    52  	ENABLE_PROCESSED_INPUT        = 0x0001
    53  	ENABLE_LINE_INPUT             = 0x0002
    54  	ENABLE_ECHO_INPUT             = 0x0004
    55  	ENABLE_WINDOW_INPUT           = 0x0008
    56  	ENABLE_MOUSE_INPUT            = 0x0010
    57  	ENABLE_INSERT_MODE            = 0x0020
    58  	ENABLE_QUICK_EDIT_MODE        = 0x0040
    59  	ENABLE_EXTENDED_FLAGS         = 0x0080
    60  	ENABLE_AUTO_POSITION          = 0x0100
    61  	ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200
    62  
    63  	ENABLE_PROCESSED_OUTPUT            = 0x0001
    64  	ENABLE_WRAP_AT_EOL_OUTPUT          = 0x0002
    65  	ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
    66  	DISABLE_NEWLINE_AUTO_RETURN        = 0x0008
    67  	ENABLE_LVB_GRID_WORLDWIDE          = 0x0010
    68  
    69  	// Character attributes
    70  	// Note:
    71  	// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
    72  	//    Clearing all foreground or background colors results in black; setting all creates white.
    73  	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
    74  	FOREGROUND_BLUE      uint16 = 0x0001
    75  	FOREGROUND_GREEN     uint16 = 0x0002
    76  	FOREGROUND_RED       uint16 = 0x0004
    77  	FOREGROUND_INTENSITY uint16 = 0x0008
    78  	FOREGROUND_MASK      uint16 = 0x000F
    79  
    80  	BACKGROUND_BLUE      uint16 = 0x0010
    81  	BACKGROUND_GREEN     uint16 = 0x0020
    82  	BACKGROUND_RED       uint16 = 0x0040
    83  	BACKGROUND_INTENSITY uint16 = 0x0080
    84  	BACKGROUND_MASK      uint16 = 0x00F0
    85  
    86  	COMMON_LVB_MASK          uint16 = 0xFF00
    87  	COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000
    88  	COMMON_LVB_UNDERSCORE    uint16 = 0x8000
    89  
    90  	// Input event types
    91  	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
    92  	KEY_EVENT                = 0x0001
    93  	MOUSE_EVENT              = 0x0002
    94  	WINDOW_BUFFER_SIZE_EVENT = 0x0004
    95  	MENU_EVENT               = 0x0008
    96  	FOCUS_EVENT              = 0x0010
    97  
    98  	// WaitForSingleObject return codes
    99  	WAIT_ABANDONED = 0x00000080
   100  	WAIT_FAILED    = 0xFFFFFFFF
   101  	WAIT_SIGNALED  = 0x0000000
   102  	WAIT_TIMEOUT   = 0x00000102
   103  
   104  	// WaitForSingleObject wait duration
   105  	WAIT_INFINITE       = 0xFFFFFFFF
   106  	WAIT_ONE_SECOND     = 1000
   107  	WAIT_HALF_SECOND    = 500
   108  	WAIT_QUARTER_SECOND = 250
   109  )
   110  
   111  // Windows API Console types
   112  // -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD)
   113  // -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment
   114  type (
   115  	CHAR_INFO struct {
   116  		UnicodeChar uint16
   117  		Attributes  uint16
   118  	}
   119  
   120  	CONSOLE_CURSOR_INFO struct {
   121  		Size    uint32
   122  		Visible int32
   123  	}
   124  
   125  	CONSOLE_SCREEN_BUFFER_INFO struct {
   126  		Size              COORD
   127  		CursorPosition    COORD
   128  		Attributes        uint16
   129  		Window            SMALL_RECT
   130  		MaximumWindowSize COORD
   131  	}
   132  
   133  	COORD struct {
   134  		X int16
   135  		Y int16
   136  	}
   137  
   138  	SMALL_RECT struct {
   139  		Left   int16
   140  		Top    int16
   141  		Right  int16
   142  		Bottom int16
   143  	}
   144  
   145  	// INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest
   146  	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
   147  	INPUT_RECORD struct {
   148  		EventType uint16
   149  		KeyEvent  KEY_EVENT_RECORD
   150  	}
   151  
   152  	KEY_EVENT_RECORD struct {
   153  		KeyDown         int32
   154  		RepeatCount     uint16
   155  		VirtualKeyCode  uint16
   156  		VirtualScanCode uint16
   157  		UnicodeChar     uint16
   158  		ControlKeyState uint32
   159  	}
   160  
   161  	WINDOW_BUFFER_SIZE struct {
   162  		Size COORD
   163  	}
   164  )
   165  
   166  // boolToBOOL converts a Go bool into a Windows int32.
   167  func boolToBOOL(f bool) int32 {
   168  	if f {
   169  		return int32(1)
   170  	} else {
   171  		return int32(0)
   172  	}
   173  }
   174  
   175  // GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor.
   176  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx.
   177  func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
   178  	r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
   179  	return checkError(r1, r2, err)
   180  }
   181  
   182  // SetConsoleCursorInfo sets the size and visiblity of the console cursor.
   183  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx.
   184  func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
   185  	r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
   186  	return checkError(r1, r2, err)
   187  }
   188  
   189  // SetConsoleCursorPosition location of the console cursor.
   190  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx.
   191  func SetConsoleCursorPosition(handle uintptr, coord COORD) error {
   192  	r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord))
   193  	use(coord)
   194  	return checkError(r1, r2, err)
   195  }
   196  
   197  // GetConsoleMode gets the console mode for given file descriptor
   198  // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx.
   199  func GetConsoleMode(handle uintptr) (mode uint32, err error) {
   200  	err = syscall.GetConsoleMode(syscall.Handle(handle), &mode)
   201  	return mode, err
   202  }
   203  
   204  // SetConsoleMode sets the console mode for given file descriptor
   205  // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
   206  func SetConsoleMode(handle uintptr, mode uint32) error {
   207  	r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0)
   208  	use(mode)
   209  	return checkError(r1, r2, err)
   210  }
   211  
   212  // GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
   213  // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx.
   214  func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
   215  	info := CONSOLE_SCREEN_BUFFER_INFO{}
   216  	err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0))
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	return &info, nil
   221  }
   222  
   223  func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error {
   224  	r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char)))
   225  	use(scrollRect)
   226  	use(clipRect)
   227  	use(destOrigin)
   228  	use(char)
   229  	return checkError(r1, r2, err)
   230  }
   231  
   232  // SetConsoleScreenBufferSize sets the size of the console screen buffer.
   233  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx.
   234  func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
   235  	r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord))
   236  	use(coord)
   237  	return checkError(r1, r2, err)
   238  }
   239  
   240  // SetConsoleTextAttribute sets the attributes of characters written to the
   241  // console screen buffer by the WriteFile or WriteConsole function.
   242  // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
   243  func SetConsoleTextAttribute(handle uintptr, attribute uint16) error {
   244  	r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)
   245  	use(attribute)
   246  	return checkError(r1, r2, err)
   247  }
   248  
   249  // SetConsoleWindowInfo sets the size and position of the console screen buffer's window.
   250  // Note that the size and location must be within and no larger than the backing console screen buffer.
   251  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx.
   252  func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error {
   253  	r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect)))
   254  	use(isAbsolute)
   255  	use(rect)
   256  	return checkError(r1, r2, err)
   257  }
   258  
   259  // WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer.
   260  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx.
   261  func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error {
   262  	r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion)))
   263  	use(buffer)
   264  	use(bufferSize)
   265  	use(bufferCoord)
   266  	return checkError(r1, r2, err)
   267  }
   268  
   269  // ReadConsoleInput reads (and removes) data from the console input buffer.
   270  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx.
   271  func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error {
   272  	r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count)))
   273  	use(buffer)
   274  	return checkError(r1, r2, err)
   275  }
   276  
   277  // WaitForSingleObject waits for the passed handle to be signaled.
   278  // It returns true if the handle was signaled; false otherwise.
   279  // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx.
   280  func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) {
   281  	r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait)))
   282  	switch r1 {
   283  	case WAIT_ABANDONED, WAIT_TIMEOUT:
   284  		return false, nil
   285  	case WAIT_SIGNALED:
   286  		return true, nil
   287  	}
   288  	use(msWait)
   289  	return false, err
   290  }
   291  
   292  // String helpers
   293  func (info CONSOLE_SCREEN_BUFFER_INFO) String() string {
   294  	return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize)
   295  }
   296  
   297  func (coord COORD) String() string {
   298  	return fmt.Sprintf("%v,%v", coord.X, coord.Y)
   299  }
   300  
   301  func (rect SMALL_RECT) String() string {
   302  	return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom)
   303  }
   304  
   305  // checkError evaluates the results of a Windows API call and returns the error if it failed.
   306  func checkError(r1, r2 uintptr, err error) error {
   307  	// Windows APIs return non-zero to indicate success
   308  	if r1 != 0 {
   309  		return nil
   310  	}
   311  
   312  	// Return the error if provided, otherwise default to EINVAL
   313  	if err != nil {
   314  		return err
   315  	}
   316  	return syscall.EINVAL
   317  }
   318  
   319  // coordToPointer converts a COORD into a uintptr (by fooling the type system).
   320  func coordToPointer(c COORD) uintptr {
   321  	// Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass.
   322  	return uintptr(*((*uint32)(unsafe.Pointer(&c))))
   323  }
   324  
   325  // use is a no-op, but the compiler cannot see that it is.
   326  // Calling use(p) ensures that p is kept live until that point.
   327  func use(p interface{}) {}
   328  

View as plain text