mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			177 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package term
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"os"
 | 
						|
	"os/signal"
 | 
						|
 | 
						|
	windowsconsole "github.com/moby/term/windows"
 | 
						|
	"golang.org/x/sys/windows"
 | 
						|
)
 | 
						|
 | 
						|
// terminalState holds the platform-specific state / console mode for the terminal.
 | 
						|
type terminalState struct {
 | 
						|
	mode uint32
 | 
						|
}
 | 
						|
 | 
						|
// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
 | 
						|
var vtInputSupported bool
 | 
						|
 | 
						|
func stdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
 | 
						|
	// Turn on VT handling on all std handles, if possible. This might
 | 
						|
	// fail, in which case we will fall back to terminal emulation.
 | 
						|
	var (
 | 
						|
		emulateStdin, emulateStdout, emulateStderr bool
 | 
						|
 | 
						|
		mode uint32
 | 
						|
	)
 | 
						|
 | 
						|
	fd := windows.Handle(os.Stdin.Fd())
 | 
						|
	if err := windows.GetConsoleMode(fd, &mode); err == nil {
 | 
						|
		// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
 | 
						|
		if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
 | 
						|
			emulateStdin = true
 | 
						|
		} else {
 | 
						|
			vtInputSupported = true
 | 
						|
		}
 | 
						|
		// Unconditionally set the console mode back even on failure because SetConsoleMode
 | 
						|
		// remembers invalid bits on input handles.
 | 
						|
		_ = windows.SetConsoleMode(fd, mode)
 | 
						|
	}
 | 
						|
 | 
						|
	fd = windows.Handle(os.Stdout.Fd())
 | 
						|
	if err := windows.GetConsoleMode(fd, &mode); err == nil {
 | 
						|
		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
 | 
						|
		if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
 | 
						|
			emulateStdout = true
 | 
						|
		} else {
 | 
						|
			_ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	fd = windows.Handle(os.Stderr.Fd())
 | 
						|
	if err := windows.GetConsoleMode(fd, &mode); err == nil {
 | 
						|
		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
 | 
						|
		if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
 | 
						|
			emulateStderr = true
 | 
						|
		} else {
 | 
						|
			_ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if emulateStdin {
 | 
						|
		h := uint32(windows.STD_INPUT_HANDLE)
 | 
						|
		stdIn = windowsconsole.NewAnsiReader(int(h))
 | 
						|
	} else {
 | 
						|
		stdIn = os.Stdin
 | 
						|
	}
 | 
						|
 | 
						|
	if emulateStdout {
 | 
						|
		h := uint32(windows.STD_OUTPUT_HANDLE)
 | 
						|
		stdOut = windowsconsole.NewAnsiWriter(int(h))
 | 
						|
	} else {
 | 
						|
		stdOut = os.Stdout
 | 
						|
	}
 | 
						|
 | 
						|
	if emulateStderr {
 | 
						|
		h := uint32(windows.STD_ERROR_HANDLE)
 | 
						|
		stdErr = windowsconsole.NewAnsiWriter(int(h))
 | 
						|
	} else {
 | 
						|
		stdErr = os.Stderr
 | 
						|
	}
 | 
						|
 | 
						|
	return stdIn, stdOut, stdErr
 | 
						|
}
 | 
						|
 | 
						|
func getFdInfo(in interface{}) (uintptr, bool) {
 | 
						|
	return windowsconsole.GetHandleInfo(in)
 | 
						|
}
 | 
						|
 | 
						|
func getWinsize(fd uintptr) (*Winsize, error) {
 | 
						|
	var info windows.ConsoleScreenBufferInfo
 | 
						|
	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	winsize := &Winsize{
 | 
						|
		Width:  uint16(info.Window.Right - info.Window.Left + 1),
 | 
						|
		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
 | 
						|
	}
 | 
						|
 | 
						|
	return winsize, nil
 | 
						|
}
 | 
						|
 | 
						|
func setWinsize(fd uintptr, ws *Winsize) error {
 | 
						|
	return fmt.Errorf("not implemented on Windows")
 | 
						|
}
 | 
						|
 | 
						|
func isTerminal(fd uintptr) bool {
 | 
						|
	var mode uint32
 | 
						|
	err := windows.GetConsoleMode(windows.Handle(fd), &mode)
 | 
						|
	return err == nil
 | 
						|
}
 | 
						|
 | 
						|
func restoreTerminal(fd uintptr, state *State) error {
 | 
						|
	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
 | 
						|
}
 | 
						|
 | 
						|
func saveState(fd uintptr) (*State, error) {
 | 
						|
	var mode uint32
 | 
						|
 | 
						|
	if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return &State{mode: mode}, nil
 | 
						|
}
 | 
						|
 | 
						|
func disableEcho(fd uintptr, state *State) error {
 | 
						|
	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
 | 
						|
	mode := state.mode
 | 
						|
	mode &^= windows.ENABLE_ECHO_INPUT
 | 
						|
	mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT
 | 
						|
	err := windows.SetConsoleMode(windows.Handle(fd), mode)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	// Register an interrupt handler to catch and restore prior state
 | 
						|
	restoreAtInterrupt(fd, state)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func setRawTerminal(fd uintptr) (*State, error) {
 | 
						|
	oldState, err := MakeRaw(fd)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Register an interrupt handler to catch and restore prior state
 | 
						|
	restoreAtInterrupt(fd, oldState)
 | 
						|
	return oldState, err
 | 
						|
}
 | 
						|
 | 
						|
func setRawTerminalOutput(fd uintptr) (*State, error) {
 | 
						|
	oldState, err := saveState(fd)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
 | 
						|
	// version of Windows.
 | 
						|
	_ = windows.SetConsoleMode(windows.Handle(fd), oldState.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
 | 
						|
	return oldState, err
 | 
						|
}
 | 
						|
 | 
						|
func restoreAtInterrupt(fd uintptr, state *State) {
 | 
						|
	sigchan := make(chan os.Signal, 1)
 | 
						|
	signal.Notify(sigchan, os.Interrupt)
 | 
						|
 | 
						|
	go func() {
 | 
						|
		_ = <-sigchan
 | 
						|
		_ = RestoreTerminal(fd, state)
 | 
						|
		os.Exit(0)
 | 
						|
	}()
 | 
						|
}
 |