...

Source file src/github.com/moby/term/term_windows.go

Documentation: github.com/moby/term

     1  package term
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"os/signal"
     8  
     9  	windowsconsole "github.com/moby/term/windows"
    10  	"golang.org/x/sys/windows"
    11  )
    12  
    13  // terminalState holds the platform-specific state / console mode for the terminal.
    14  type terminalState struct {
    15  	mode uint32
    16  }
    17  
    18  // vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
    19  var vtInputSupported bool
    20  
    21  func stdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
    22  	// Turn on VT handling on all std handles, if possible. This might
    23  	// fail, in which case we will fall back to terminal emulation.
    24  	var (
    25  		emulateStdin, emulateStdout, emulateStderr bool
    26  
    27  		mode uint32
    28  	)
    29  
    30  	fd := windows.Handle(os.Stdin.Fd())
    31  	if err := windows.GetConsoleMode(fd, &mode); err == nil {
    32  		// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
    33  		if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
    34  			emulateStdin = true
    35  		} else {
    36  			vtInputSupported = true
    37  		}
    38  		// Unconditionally set the console mode back even on failure because SetConsoleMode
    39  		// remembers invalid bits on input handles.
    40  		_ = windows.SetConsoleMode(fd, mode)
    41  	}
    42  
    43  	fd = windows.Handle(os.Stdout.Fd())
    44  	if err := windows.GetConsoleMode(fd, &mode); err == nil {
    45  		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
    46  		if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
    47  			emulateStdout = true
    48  		} else {
    49  			_ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
    50  		}
    51  	}
    52  
    53  	fd = windows.Handle(os.Stderr.Fd())
    54  	if err := windows.GetConsoleMode(fd, &mode); err == nil {
    55  		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
    56  		if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
    57  			emulateStderr = true
    58  		} else {
    59  			_ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
    60  		}
    61  	}
    62  
    63  	if emulateStdin {
    64  		h := uint32(windows.STD_INPUT_HANDLE)
    65  		stdIn = windowsconsole.NewAnsiReader(int(h))
    66  	} else {
    67  		stdIn = os.Stdin
    68  	}
    69  
    70  	if emulateStdout {
    71  		h := uint32(windows.STD_OUTPUT_HANDLE)
    72  		stdOut = windowsconsole.NewAnsiWriter(int(h))
    73  	} else {
    74  		stdOut = os.Stdout
    75  	}
    76  
    77  	if emulateStderr {
    78  		h := uint32(windows.STD_ERROR_HANDLE)
    79  		stdErr = windowsconsole.NewAnsiWriter(int(h))
    80  	} else {
    81  		stdErr = os.Stderr
    82  	}
    83  
    84  	return stdIn, stdOut, stdErr
    85  }
    86  
    87  func getFdInfo(in interface{}) (uintptr, bool) {
    88  	return windowsconsole.GetHandleInfo(in)
    89  }
    90  
    91  func getWinsize(fd uintptr) (*Winsize, error) {
    92  	var info windows.ConsoleScreenBufferInfo
    93  	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	winsize := &Winsize{
    98  		Width:  uint16(info.Window.Right - info.Window.Left + 1),
    99  		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
   100  	}
   101  
   102  	return winsize, nil
   103  }
   104  
   105  func setWinsize(fd uintptr, ws *Winsize) error {
   106  	return fmt.Errorf("not implemented on Windows")
   107  }
   108  
   109  func isTerminal(fd uintptr) bool {
   110  	var mode uint32
   111  	err := windows.GetConsoleMode(windows.Handle(fd), &mode)
   112  	return err == nil
   113  }
   114  
   115  func restoreTerminal(fd uintptr, state *State) error {
   116  	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
   117  }
   118  
   119  func saveState(fd uintptr) (*State, error) {
   120  	var mode uint32
   121  
   122  	if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	return &State{mode: mode}, nil
   127  }
   128  
   129  func disableEcho(fd uintptr, state *State) error {
   130  	// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
   131  	mode := state.mode
   132  	mode &^= windows.ENABLE_ECHO_INPUT
   133  	mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT
   134  	err := windows.SetConsoleMode(windows.Handle(fd), mode)
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	// Register an interrupt handler to catch and restore prior state
   140  	restoreAtInterrupt(fd, state)
   141  	return nil
   142  }
   143  
   144  func setRawTerminal(fd uintptr) (*State, error) {
   145  	oldState, err := MakeRaw(fd)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	// Register an interrupt handler to catch and restore prior state
   151  	restoreAtInterrupt(fd, oldState)
   152  	return oldState, err
   153  }
   154  
   155  func setRawTerminalOutput(fd uintptr) (*State, error) {
   156  	oldState, err := saveState(fd)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
   162  	// version of Windows.
   163  	_ = windows.SetConsoleMode(windows.Handle(fd), oldState.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
   164  	return oldState, err
   165  }
   166  
   167  func restoreAtInterrupt(fd uintptr, state *State) {
   168  	sigchan := make(chan os.Signal, 1)
   169  	signal.Notify(sigchan, os.Interrupt)
   170  
   171  	go func() {
   172  		_ = <-sigchan
   173  		_ = RestoreTerminal(fd, state)
   174  		os.Exit(0)
   175  	}()
   176  }
   177  

View as plain text