123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- package console
- import (
- "fmt"
- "os"
- "github.com/pkg/errors"
- "golang.org/x/sys/windows"
- )
- var (
- vtInputSupported bool
- ErrNotImplemented = errors.New("not implemented")
- )
- func (m *master) initStdios() {
- m.in = windows.Handle(os.Stdin.Fd())
- if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil {
- // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
- if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
- vtInputSupported = true
- }
- // Unconditionally set the console mode back even on failure because SetConsoleMode
- // remembers invalid bits on input handles.
- windows.SetConsoleMode(m.in, m.inMode)
- } else {
- fmt.Printf("failed to get console mode for stdin: %v\n", err)
- }
- m.out = windows.Handle(os.Stdout.Fd())
- if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil {
- if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
- m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
- } else {
- windows.SetConsoleMode(m.out, m.outMode)
- }
- } else {
- fmt.Printf("failed to get console mode for stdout: %v\n", err)
- }
- m.err = windows.Handle(os.Stderr.Fd())
- if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil {
- if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
- m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
- } else {
- windows.SetConsoleMode(m.err, m.errMode)
- }
- } else {
- fmt.Printf("failed to get console mode for stderr: %v\n", err)
- }
- }
- type master struct {
- in windows.Handle
- inMode uint32
- out windows.Handle
- outMode uint32
- err windows.Handle
- errMode uint32
- }
- func (m *master) SetRaw() error {
- if err := makeInputRaw(m.in, m.inMode); err != nil {
- return err
- }
- // Set StdOut and StdErr to raw mode, we ignore failures since
- // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
- // Windows.
- windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
- windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
- return nil
- }
- func (m *master) Reset() error {
- for _, s := range []struct {
- fd windows.Handle
- mode uint32
- }{
- {m.in, m.inMode},
- {m.out, m.outMode},
- {m.err, m.errMode},
- } {
- if err := windows.SetConsoleMode(s.fd, s.mode); err != nil {
- return errors.Wrap(err, "unable to restore console mode")
- }
- }
- return nil
- }
- func (m *master) Size() (WinSize, error) {
- var info windows.ConsoleScreenBufferInfo
- err := windows.GetConsoleScreenBufferInfo(m.out, &info)
- if err != nil {
- return WinSize{}, errors.Wrap(err, "unable to get console info")
- }
- winsize := WinSize{
- Width: uint16(info.Window.Right - info.Window.Left + 1),
- Height: uint16(info.Window.Bottom - info.Window.Top + 1),
- }
- return winsize, nil
- }
- func (m *master) Resize(ws WinSize) error {
- return ErrNotImplemented
- }
- func (m *master) ResizeFrom(c Console) error {
- return ErrNotImplemented
- }
- func (m *master) DisableEcho() error {
- mode := m.inMode &^ windows.ENABLE_ECHO_INPUT
- mode |= windows.ENABLE_PROCESSED_INPUT
- mode |= windows.ENABLE_LINE_INPUT
- if err := windows.SetConsoleMode(m.in, mode); err != nil {
- return errors.Wrap(err, "unable to set console to disable echo")
- }
- return nil
- }
- func (m *master) Close() error {
- return nil
- }
- func (m *master) Read(b []byte) (int, error) {
- panic("not implemented on windows")
- }
- func (m *master) Write(b []byte) (int, error) {
- panic("not implemented on windows")
- }
- func (m *master) Fd() uintptr {
- return uintptr(m.in)
- }
- // on windows, console can only be made from os.Std{in,out,err}, hence there
- // isnt a single name here we can use. Return a dummy "console" value in this
- // case should be sufficient.
- func (m *master) Name() string {
- return "console"
- }
- // makeInputRaw puts the terminal (Windows Console) connected to the given
- // file descriptor into raw mode
- func makeInputRaw(fd windows.Handle, mode uint32) error {
- // See
- // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
- // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
- // Disable these modes
- mode &^= windows.ENABLE_ECHO_INPUT
- mode &^= windows.ENABLE_LINE_INPUT
- mode &^= windows.ENABLE_MOUSE_INPUT
- mode &^= windows.ENABLE_WINDOW_INPUT
- mode &^= windows.ENABLE_PROCESSED_INPUT
- // Enable these modes
- mode |= windows.ENABLE_EXTENDED_FLAGS
- mode |= windows.ENABLE_INSERT_MODE
- mode |= windows.ENABLE_QUICK_EDIT_MODE
- if vtInputSupported {
- mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
- }
- if err := windows.SetConsoleMode(fd, mode); err != nil {
- return errors.Wrap(err, "unable to set console to raw mode")
- }
- return nil
- }
- func checkConsole(f *os.File) error {
- var mode uint32
- if err := windows.GetConsoleMode(windows.Handle(f.Fd()), &mode); err != nil {
- return err
- }
- return nil
- }
- func newMaster(f *os.File) (Console, error) {
- if f != os.Stdin && f != os.Stdout && f != os.Stderr {
- return nil, errors.New("creating a console from a file is not supported on windows")
- }
- m := &master{}
- m.initStdios()
- return m, nil
- }
|