aminal/platform/winpty.go

247 lines
6.7 KiB
Go

// +build windows,cgo
package platform
import (
"errors"
"github.com/MaxRis/w32"
"syscall"
)
// #include "windows.h"
//
// /* Until we can specify the platform SDK and target version for Windows.h *
// * without breaking our ability to gracefully display an error message, these *
// * definitions will be copied from the platform SDK headers and made to work. *
// */
//
// typedef HRESULT (* CreatePseudoConsoleProcType)( COORD, HANDLE, HANDLE, DWORD, uintptr_t * );
// typedef HRESULT (* ResizePseudoConsoleProcType)( uintptr_t, COORD );
// typedef HRESULT (* ClosePseudoConsoleProcType)( uintptr_t );
//
//
// CreatePseudoConsoleProcType pfnCreatePseudoConsole = NULL;
// ResizePseudoConsoleProcType pfnResizePseudoConsole = NULL;
// ClosePseudoConsoleProcType pfnClosePseudoConsole = NULL;
//
// HMODULE hLibKernel32_Kern = NULL;
//
// DWORD initPtyKernFuncs()
// {
// hLibKernel32_Kern = LoadLibrary( "kernel32.dll" );
// if( hLibKernel32_Kern == NULL )
// {
// return -1;
// }
//
// pfnCreatePseudoConsole = (CreatePseudoConsoleProcType) GetProcAddress(hLibKernel32_Kern, "CreatePseudoConsole" );
// if( pfnCreatePseudoConsole == NULL )
// {
// return -1;
// }
//
// pfnResizePseudoConsole = (ResizePseudoConsoleProcType) GetProcAddress(hLibKernel32_Kern, "ResizePseudoConsole" );
// if( pfnResizePseudoConsole == NULL )
// {
// return -1;
// }
//
// pfnClosePseudoConsole = (ClosePseudoConsoleProcType) GetProcAddress(hLibKernel32_Kern, "ClosePseudoConsole" );
// if( pfnClosePseudoConsole == NULL )
// {
// return -1;
// }
//
// return 0;
// }
//
// DWORD createPtyHelper( int xSize, int ySize, HANDLE input, HANDLE output, DWORD flags, uintptr_t * phPC )
// {
// COORD size;
// size.X = xSize;
// size.Y = ySize;
// return (DWORD) (*pfnCreatePseudoConsole)( size, input, output, flags, phPC );
// }
//
// DWORD resizePtyHelper( uintptr_t hpc, int xSize, int ySize )
// {
// COORD size;
// size.X = xSize;
// size.Y = ySize;
// return (DWORD) (*pfnResizePseudoConsole)( hpc, size );
// }
//
// DWORD closePtyHelper( uintptr_t hpc )
// {
// return (DWORD) (*pfnClosePseudoConsole)( hpc );
// }
//
// int hr_succeeded( DWORD hResult )
// {
// return SUCCEEDED( hResult );
// }
import "C"
var ptyInitSucceeded = false
func init() {
ret := int(C.initPtyKernFuncs())
ptyInitSucceeded = (ret == 0)
}
type winConPty struct {
inPipe syscall.Handle
outPipe syscall.Handle
innerInPipe syscall.Handle
innerOutPipe syscall.Handle
hcon uintptr
processID uint32 // required to obtain old-style console handle (for standard console functions)
platformDependentSettings PlatformDependentSettings
}
func (pty *winConPty) Read(p []byte) (n int, err error) {
return syscall.Read(pty.inPipe, p)
}
func (pty *winConPty) Write(p []byte) (n int, err error) {
return syscall.Write(pty.outPipe, p)
}
func (pty *winConPty) Close() error {
C.closePtyHelper(C.uintptr_t(pty.hcon))
err := syscall.CloseHandle(pty.inPipe)
if err != nil {
return err
}
err = syscall.CloseHandle(pty.outPipe)
if err != nil {
return err
}
err = syscall.CloseHandle(pty.innerInPipe)
if err != nil {
return err
}
err = syscall.CloseHandle(pty.innerOutPipe)
if err != nil {
return err
}
pty.hcon = 0
return nil
}
func (pty *winConPty) CreateGuestProcess(imagePath string) (Process, error) {
process, err := createPtyChildProcess(imagePath, pty.hcon)
if err != nil {
return nil, err
}
pty.processID = process.processID
return process, nil
}
func (pty *winConPty) Resize(x, y int) error {
cret := C.resizePtyHelper(C.uintptr_t(pty.hcon), C.int(x), C.int(y))
if int(C.hr_succeeded(cret)) == 0 {
return errors.New("Failed to resize ConPTY")
}
return nil
}
func (pty *winConPty) GetPlatformDependentSettings() PlatformDependentSettings {
return pty.platformDependentSettings
}
func (pty *winConPty) Clear() {
C.FreeConsole()
defer C.AttachConsole(^C.DWORD(0)) // attach to parent process console
if C.AttachConsole(C.DWORD(pty.processID)) == 0 {
return
}
defer C.FreeConsole()
hConsole := C.GetStdHandle(C.STD_OUTPUT_HANDLE)
var coordScreen C.COORD
coordScreen.X = 0
coordScreen.Y = 0
var cCharsWritten C.DWORD
var csbi C.CONSOLE_SCREEN_BUFFER_INFO
// Get the number of character cells in the current buffer.
if C.GetConsoleScreenBufferInfo(hConsole, &csbi) != C.BOOL(0) {
dwConSize := C.DWORD(csbi.dwSize.X * csbi.dwSize.Y)
// Fill the entire screen with blanks.
C.FillConsoleOutputCharacterA(hConsole, // Handle to console screen buffer
' ', // Character to write to the buffer
dwConSize, // Number of cells to write
coordScreen, // Coordinates of first cell
&cCharsWritten) // Receive number of characters written
// Get the current text attribute.
if C.GetConsoleScreenBufferInfo(hConsole, &csbi) != C.BOOL(0) {
// Set the buffer's attributes accordingly.
C.FillConsoleOutputAttribute(hConsole, // Handle to console screen buffer
csbi.wAttributes, // Character attributes to use
dwConSize, // Number of cells to set attribute
coordScreen, // Coordinates of first cell
&cCharsWritten) // Receive number of characters written
}
}
// Put the cursor at its home coordinates.
C.SetConsoleCursorPosition(hConsole, coordScreen)
}
// NewPty creates a new instance of a Pty implementation for Windows on a newly allocated ConPTY
func NewPty(x, y int) (pty Pty, err error) {
if !ptyInitSucceeded {
w32.MessageBox(0, "Aminal requires APIs that are only available on Windows 10 1809 (October 2018 Update) or above. Please upgrade", "Aminal", 0)
return nil, errors.New("Windows PseudoConsole API unavailable on this version of Windows")
}
pty = nil
var inputReadSide, inputWriteSide syscall.Handle
var outputReadSide, outputWriteSide syscall.Handle
err = syscall.CreatePipe(&inputReadSide, &inputWriteSide, nil, 0)
if err != nil {
return
}
err = syscall.CreatePipe(&outputReadSide, &outputWriteSide, nil, 0)
if err != nil {
return
}
var hc C.uintptr_t
cret := C.createPtyHelper(C.int(x), C.int(y), C.HANDLE(inputReadSide), C.HANDLE(outputWriteSide), 0, &hc)
ret := int(cret)
if ret != 0 {
return nil, errors.New("Failed to allocate a ConPTY instance")
}
pty = &winConPty{
inPipe: outputReadSide,
outPipe: inputWriteSide,
innerInPipe: inputReadSide,
innerOutPipe: outputWriteSide,
hcon: uintptr(hc),
platformDependentSettings: PlatformDependentSettings{
OSCTerminators: map[rune]struct{}{0x00: {}, 0x07: {}},
},
}
return pty, nil
}