1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #35056 from tklauser/win-console-mode-consts

Use windows console mode constants from Azure/go-ansiterm
This commit is contained in:
Sebastiaan van Stijn 2017-10-05 00:41:22 +02:00 committed by GitHub
commit 1c4fad8135
15 changed files with 173 additions and 150 deletions

View file

@ -23,14 +23,7 @@ type Winsize struct {
Width uint16 Width uint16
} }
const ( // vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
enableVirtualTerminalInput = 0x0200
enableVirtualTerminalProcessing = 0x0004
disableNewlineAutoReturn = 0x0008
)
// vtInputSupported is true if enableVirtualTerminalInput is supported by the console
var vtInputSupported bool var vtInputSupported bool
// StdStreams returns the standard streams (stdin, stdout, stderr). // StdStreams returns the standard streams (stdin, stdout, stderr).
@ -40,8 +33,8 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
var emulateStdin, emulateStdout, emulateStderr bool var emulateStdin, emulateStdout, emulateStderr bool
fd := os.Stdin.Fd() fd := os.Stdin.Fd()
if mode, err := winterm.GetConsoleMode(fd); err == nil { if mode, err := winterm.GetConsoleMode(fd); err == nil {
// Validate that enableVirtualTerminalInput is supported, but do not set it. // Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalInput); err != nil { if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
emulateStdin = true emulateStdin = true
} else { } else {
vtInputSupported = true vtInputSupported = true
@ -53,21 +46,21 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
fd = os.Stdout.Fd() fd = os.Stdout.Fd()
if mode, err := winterm.GetConsoleMode(fd); err == nil { if mode, err := winterm.GetConsoleMode(fd); err == nil {
// Validate disableNewlineAutoReturn is supported, but do not set it. // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil { if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
emulateStdout = true emulateStdout = true
} else { } else {
winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing) winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
} }
} }
fd = os.Stderr.Fd() fd = os.Stderr.Fd()
if mode, err := winterm.GetConsoleMode(fd); err == nil { if mode, err := winterm.GetConsoleMode(fd); err == nil {
// Validate disableNewlineAutoReturn is supported, but do not set it. // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil { if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
emulateStderr = true emulateStderr = true
} else { } else {
winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing) winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
} }
} }
@ -183,9 +176,9 @@ func SetRawTerminalOutput(fd uintptr) (*State, error) {
return nil, err return nil, err
} }
// Ignore failures, since disableNewlineAutoReturn might not be supported on this // Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
// version of Windows. // version of Windows.
winterm.SetConsoleMode(fd, state.mode|disableNewlineAutoReturn) winterm.SetConsoleMode(fd, state.mode|winterm.DISABLE_NEWLINE_AUTO_RETURN)
return state, err return state, err
} }
@ -215,7 +208,7 @@ func MakeRaw(fd uintptr) (*State, error) {
mode |= winterm.ENABLE_INSERT_MODE mode |= winterm.ENABLE_INSERT_MODE
mode |= winterm.ENABLE_QUICK_EDIT_MODE mode |= winterm.ENABLE_QUICK_EDIT_MODE
if vtInputSupported { if vtInputSupported {
mode |= enableVirtualTerminalInput mode |= winterm.ENABLE_VIRTUAL_TERMINAL_INPUT
} }
err = winterm.SetConsoleMode(fd, mode) err = winterm.SetConsoleMode(fd, mode)

View file

@ -1,5 +1,5 @@
# the following lines are in sorted order, FYI # the following lines are in sorted order, FYI
github.com/Azure/go-ansiterm 19f72df4d05d31cbe1c56bfc8045c96babff6c7e github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
github.com/Microsoft/hcsshim v0.6.5 github.com/Microsoft/hcsshim v0.6.5
github.com/Microsoft/go-winio v0.4.5 github.com/Microsoft/go-winio v0.4.5
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76

View file

@ -5,7 +5,7 @@ type csiEntryState struct {
} }
func (csiState csiEntryState) Handle(b byte) (s state, e error) { func (csiState csiEntryState) Handle(b byte) (s state, e error) {
logger.Infof("CsiEntry::Handle %#x", b) csiState.parser.logf("CsiEntry::Handle %#x", b)
nextState, err := csiState.baseState.Handle(b) nextState, err := csiState.baseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
@ -25,7 +25,7 @@ func (csiState csiEntryState) Handle(b byte) (s state, e error) {
} }
func (csiState csiEntryState) Transition(s state) error { func (csiState csiEntryState) Transition(s state) error {
logger.Infof("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name()) csiState.parser.logf("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name())
csiState.baseState.Transition(s) csiState.baseState.Transition(s)
switch s { switch s {

View file

@ -5,7 +5,7 @@ type csiParamState struct {
} }
func (csiState csiParamState) Handle(b byte) (s state, e error) { func (csiState csiParamState) Handle(b byte) (s state, e error) {
logger.Infof("CsiParam::Handle %#x", b) csiState.parser.logf("CsiParam::Handle %#x", b)
nextState, err := csiState.baseState.Handle(b) nextState, err := csiState.baseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
@ -26,7 +26,7 @@ func (csiState csiParamState) Handle(b byte) (s state, e error) {
} }
func (csiState csiParamState) Transition(s state) error { func (csiState csiParamState) Transition(s state) error {
logger.Infof("CsiParam::Transition %s --> %s", csiState.Name(), s.Name()) csiState.parser.logf("CsiParam::Transition %s --> %s", csiState.Name(), s.Name())
csiState.baseState.Transition(s) csiState.baseState.Transition(s)
switch s { switch s {

View file

@ -5,7 +5,7 @@ type escapeIntermediateState struct {
} }
func (escState escapeIntermediateState) Handle(b byte) (s state, e error) { func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
logger.Infof("escapeIntermediateState::Handle %#x", b) escState.parser.logf("escapeIntermediateState::Handle %#x", b)
nextState, err := escState.baseState.Handle(b) nextState, err := escState.baseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
@ -24,7 +24,7 @@ func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
} }
func (escState escapeIntermediateState) Transition(s state) error { func (escState escapeIntermediateState) Transition(s state) error {
logger.Infof("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name()) escState.parser.logf("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name())
escState.baseState.Transition(s) escState.baseState.Transition(s)
switch s { switch s {

View file

@ -5,7 +5,7 @@ type escapeState struct {
} }
func (escState escapeState) Handle(b byte) (s state, e error) { func (escState escapeState) Handle(b byte) (s state, e error) {
logger.Infof("escapeState::Handle %#x", b) escState.parser.logf("escapeState::Handle %#x", b)
nextState, err := escState.baseState.Handle(b) nextState, err := escState.baseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
@ -28,7 +28,7 @@ func (escState escapeState) Handle(b byte) (s state, e error) {
} }
func (escState escapeState) Transition(s state) error { func (escState escapeState) Transition(s state) error {
logger.Infof("Escape::Transition %s --> %s", escState.Name(), s.Name()) escState.parser.logf("Escape::Transition %s --> %s", escState.Name(), s.Name())
escState.baseState.Transition(s) escState.baseState.Transition(s)
switch s { switch s {

View file

@ -5,7 +5,7 @@ type oscStringState struct {
} }
func (oscState oscStringState) Handle(b byte) (s state, e error) { func (oscState oscStringState) Handle(b byte) (s state, e error) {
logger.Infof("OscString::Handle %#x", b) oscState.parser.logf("OscString::Handle %#x", b)
nextState, err := oscState.baseState.Handle(b) nextState, err := oscState.baseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err

View file

@ -2,14 +2,10 @@ package ansiterm
import ( import (
"errors" "errors"
"io/ioutil" "log"
"os" "os"
"github.com/sirupsen/logrus"
) )
var logger *logrus.Logger
type AnsiParser struct { type AnsiParser struct {
currState state currState state
eventHandler AnsiEventHandler eventHandler AnsiEventHandler
@ -23,50 +19,69 @@ type AnsiParser struct {
ground state ground state
oscString state oscString state
stateMap []state stateMap []state
logf func(string, ...interface{})
} }
func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser { type Option func(*AnsiParser)
logFile := ioutil.Discard
if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" { func WithLogf(f func(string, ...interface{})) Option {
logFile, _ = os.Create("ansiParser.log") return func(ap *AnsiParser) {
ap.logf = f
} }
}
logger = &logrus.Logger{ func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser {
Out: logFile, ap := &AnsiParser{
Formatter: new(logrus.TextFormatter),
Level: logrus.InfoLevel,
}
parser := &AnsiParser{
eventHandler: evtHandler, eventHandler: evtHandler,
context: &ansiContext{}, context: &ansiContext{},
} }
for _, o := range opts {
parser.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: parser}} o(ap)
parser.csiParam = csiParamState{baseState{name: "CsiParam", parser: parser}}
parser.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: parser}}
parser.escape = escapeState{baseState{name: "Escape", parser: parser}}
parser.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: parser}}
parser.error = errorState{baseState{name: "Error", parser: parser}}
parser.ground = groundState{baseState{name: "Ground", parser: parser}}
parser.oscString = oscStringState{baseState{name: "OscString", parser: parser}}
parser.stateMap = []state{
parser.csiEntry,
parser.csiParam,
parser.dcsEntry,
parser.escape,
parser.escapeIntermediate,
parser.error,
parser.ground,
parser.oscString,
} }
parser.currState = getState(initialState, parser.stateMap) if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
logFile, _ := os.Create("ansiParser.log")
logger := log.New(logFile, "", log.LstdFlags)
if ap.logf != nil {
l := ap.logf
ap.logf = func(s string, v ...interface{}) {
l(s, v...)
logger.Printf(s, v...)
}
} else {
ap.logf = logger.Printf
}
}
logger.Infof("CreateParser: parser %p", parser) if ap.logf == nil {
return parser ap.logf = func(string, ...interface{}) {}
}
ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}}
ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}}
ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}}
ap.escape = escapeState{baseState{name: "Escape", parser: ap}}
ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}}
ap.error = errorState{baseState{name: "Error", parser: ap}}
ap.ground = groundState{baseState{name: "Ground", parser: ap}}
ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}}
ap.stateMap = []state{
ap.csiEntry,
ap.csiParam,
ap.dcsEntry,
ap.escape,
ap.escapeIntermediate,
ap.error,
ap.ground,
ap.oscString,
}
ap.currState = getState(initialState, ap.stateMap)
ap.logf("CreateParser: parser %p", ap)
return ap
} }
func getState(name string, states []state) state { func getState(name string, states []state) state {
@ -97,7 +112,7 @@ func (ap *AnsiParser) handle(b byte) error {
} }
if newState == nil { if newState == nil {
logger.Warning("newState is nil") ap.logf("WARNING: newState is nil")
return errors.New("New state of 'nil' is invalid.") return errors.New("New state of 'nil' is invalid.")
} }
@ -111,23 +126,23 @@ func (ap *AnsiParser) handle(b byte) error {
} }
func (ap *AnsiParser) changeState(newState state) error { func (ap *AnsiParser) changeState(newState state) error {
logger.Infof("ChangeState %s --> %s", ap.currState.Name(), newState.Name()) ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
// Exit old state // Exit old state
if err := ap.currState.Exit(); err != nil { if err := ap.currState.Exit(); err != nil {
logger.Infof("Exit state '%s' failed with : '%v'", ap.currState.Name(), err) ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err)
return err return err
} }
// Perform transition action // Perform transition action
if err := ap.currState.Transition(newState); err != nil { if err := ap.currState.Transition(newState); err != nil {
logger.Infof("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err) ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err)
return err return err
} }
// Enter new state // Enter new state
if err := newState.Enter(); err != nil { if err := newState.Enter(); err != nil {
logger.Infof("Enter state '%s' failed with: '%v'", newState.Name(), err) ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err)
return err return err
} }

View file

@ -27,7 +27,6 @@ func parseParams(bytes []byte) ([]string, error) {
params = append(params, s) params = append(params, s)
} }
logger.Infof("Parsed params: %v with length: %d", params, len(params))
return params, nil return params, nil
} }
@ -37,7 +36,6 @@ func parseCmd(context ansiContext) (string, error) {
func getInt(params []string, dflt int) int { func getInt(params []string, dflt int) int {
i := getInts(params, 1, dflt)[0] i := getInts(params, 1, dflt)[0]
logger.Infof("getInt: %v", i)
return i return i
} }
@ -60,8 +58,6 @@ func getInts(params []string, minCount int, dflt int) []int {
} }
} }
logger.Infof("getInts: %v", ints)
return ints return ints
} }

View file

@ -1,19 +1,15 @@
package ansiterm package ansiterm
import (
"fmt"
)
func (ap *AnsiParser) collectParam() error { func (ap *AnsiParser) collectParam() error {
currChar := ap.context.currentChar currChar := ap.context.currentChar
logger.Infof("collectParam %#x", currChar) ap.logf("collectParam %#x", currChar)
ap.context.paramBuffer = append(ap.context.paramBuffer, currChar) ap.context.paramBuffer = append(ap.context.paramBuffer, currChar)
return nil return nil
} }
func (ap *AnsiParser) collectInter() error { func (ap *AnsiParser) collectInter() error {
currChar := ap.context.currentChar currChar := ap.context.currentChar
logger.Infof("collectInter %#x", currChar) ap.logf("collectInter %#x", currChar)
ap.context.paramBuffer = append(ap.context.interBuffer, currChar) ap.context.paramBuffer = append(ap.context.interBuffer, currChar)
return nil return nil
} }
@ -21,8 +17,8 @@ func (ap *AnsiParser) collectInter() error {
func (ap *AnsiParser) escDispatch() error { func (ap *AnsiParser) escDispatch() error {
cmd, _ := parseCmd(*ap.context) cmd, _ := parseCmd(*ap.context)
intermeds := ap.context.interBuffer intermeds := ap.context.interBuffer
logger.Infof("escDispatch currentChar: %#x", ap.context.currentChar) ap.logf("escDispatch currentChar: %#x", ap.context.currentChar)
logger.Infof("escDispatch: %v(%v)", cmd, intermeds) ap.logf("escDispatch: %v(%v)", cmd, intermeds)
switch cmd { switch cmd {
case "D": // IND case "D": // IND
@ -43,8 +39,9 @@ func (ap *AnsiParser) escDispatch() error {
func (ap *AnsiParser) csiDispatch() error { func (ap *AnsiParser) csiDispatch() error {
cmd, _ := parseCmd(*ap.context) cmd, _ := parseCmd(*ap.context)
params, _ := parseParams(ap.context.paramBuffer) params, _ := parseParams(ap.context.paramBuffer)
ap.logf("Parsed params: %v with length: %d", params, len(params))
logger.Infof("csiDispatch: %v(%v)", cmd, params) ap.logf("csiDispatch: %v(%v)", cmd, params)
switch cmd { switch cmd {
case "@": case "@":
@ -102,7 +99,7 @@ func (ap *AnsiParser) csiDispatch() error {
top, bottom := ints[0], ints[1] top, bottom := ints[0], ints[1]
return ap.eventHandler.DECSTBM(top, bottom) return ap.eventHandler.DECSTBM(top, bottom)
default: default:
logger.Errorf(fmt.Sprintf("Unsupported CSI command: '%s', with full context: %v", cmd, ap.context)) ap.logf("ERROR: Unsupported CSI command: '%s', with full context: %v", cmd, ap.context)
return nil return nil
} }

View file

@ -175,7 +175,7 @@ func GetStdFile(nFile int) (*os.File, uintptr) {
fd, err := syscall.GetStdHandle(nFile) fd, err := syscall.GetStdHandle(nFile)
if err != nil { if err != nil {
panic(fmt.Errorf("Invalid standard handle indentifier: %v -- %v", nFile, err)) panic(fmt.Errorf("Invalid standard handle identifier: %v -- %v", nFile, err))
} }
return file, uintptr(fd) return file, uintptr(fd)

View file

@ -49,17 +49,22 @@ var (
const ( const (
// Console modes // Console modes
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
ENABLE_PROCESSED_INPUT = 0x0001 ENABLE_PROCESSED_INPUT = 0x0001
ENABLE_LINE_INPUT = 0x0002 ENABLE_LINE_INPUT = 0x0002
ENABLE_ECHO_INPUT = 0x0004 ENABLE_ECHO_INPUT = 0x0004
ENABLE_WINDOW_INPUT = 0x0008 ENABLE_WINDOW_INPUT = 0x0008
ENABLE_MOUSE_INPUT = 0x0010 ENABLE_MOUSE_INPUT = 0x0010
ENABLE_INSERT_MODE = 0x0020 ENABLE_INSERT_MODE = 0x0020
ENABLE_QUICK_EDIT_MODE = 0x0040 ENABLE_QUICK_EDIT_MODE = 0x0040
ENABLE_EXTENDED_FLAGS = 0x0080 ENABLE_EXTENDED_FLAGS = 0x0080
ENABLE_AUTO_POSITION = 0x0100
ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200
ENABLE_PROCESSED_OUTPUT = 0x0001 ENABLE_PROCESSED_OUTPUT = 0x0001
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002 ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
DISABLE_NEWLINE_AUTO_RETURN = 0x0008
ENABLE_LVB_GRID_WORLDWIDE = 0x0010
// Character attributes // Character attributes
// Note: // Note:

View file

@ -34,7 +34,7 @@ func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL
if err != nil { if err != nil {
return err return err
} }
logger.Infof("Cursor position set: (%d, %d)", position.X, position.Y) h.logf("Cursor position set: (%d, %d)", position.X, position.Y)
return err return err
} }

View file

@ -50,8 +50,8 @@ func (h *windowsAnsiEventHandler) insertLines(param int) error {
// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates. // scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates.
func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error { func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error {
logger.Infof("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom) h.logf("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom)
logger.Infof("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom) h.logf("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
// Copy from and clip to the scroll region (full buffer width) // Copy from and clip to the scroll region (full buffer width)
scrollRect := SMALL_RECT{ scrollRect := SMALL_RECT{

View file

@ -4,16 +4,13 @@ package winterm
import ( import (
"bytes" "bytes"
"io/ioutil" "log"
"os" "os"
"strconv" "strconv"
"github.com/Azure/go-ansiterm" "github.com/Azure/go-ansiterm"
"github.com/sirupsen/logrus"
) )
var logger *logrus.Logger
type windowsAnsiEventHandler struct { type windowsAnsiEventHandler struct {
fd uintptr fd uintptr
file *os.File file *os.File
@ -28,32 +25,52 @@ type windowsAnsiEventHandler struct {
marginByte byte marginByte byte
curInfo *CONSOLE_SCREEN_BUFFER_INFO curInfo *CONSOLE_SCREEN_BUFFER_INFO
curPos COORD curPos COORD
logf func(string, ...interface{})
} }
func CreateWinEventHandler(fd uintptr, file *os.File) ansiterm.AnsiEventHandler { type Option func(*windowsAnsiEventHandler)
logFile := ioutil.Discard
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { func WithLogf(f func(string, ...interface{})) Option {
logFile, _ = os.Create("winEventHandler.log") return func(w *windowsAnsiEventHandler) {
} w.logf = f
logger = &logrus.Logger{
Out: logFile,
Formatter: new(logrus.TextFormatter),
Level: logrus.DebugLevel,
} }
}
func CreateWinEventHandler(fd uintptr, file *os.File, opts ...Option) ansiterm.AnsiEventHandler {
infoReset, err := GetConsoleScreenBufferInfo(fd) infoReset, err := GetConsoleScreenBufferInfo(fd)
if err != nil { if err != nil {
return nil return nil
} }
return &windowsAnsiEventHandler{ h := &windowsAnsiEventHandler{
fd: fd, fd: fd,
file: file, file: file,
infoReset: infoReset, infoReset: infoReset,
attributes: infoReset.Attributes, attributes: infoReset.Attributes,
} }
for _, o := range opts {
o(h)
}
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
logFile, _ := os.Create("winEventHandler.log")
logger := log.New(logFile, "", log.LstdFlags)
if h.logf != nil {
l := h.logf
h.logf = func(s string, v ...interface{}) {
l(s, v...)
logger.Printf(s, v...)
}
} else {
h.logf = logger.Printf
}
}
if h.logf == nil {
h.logf = func(string, ...interface{}) {}
}
return h
} }
type scrollRegion struct { type scrollRegion struct {
@ -96,7 +113,7 @@ func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return false, err return false, err
} }
logger.Info("Simulating LF inside scroll region") h.logf("Simulating LF inside scroll region")
if err := h.scrollUp(1); err != nil { if err := h.scrollUp(1); err != nil {
return false, err return false, err
} }
@ -119,7 +136,7 @@ func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
} else { } else {
// The cursor is at the bottom of the screen but outside the scroll // The cursor is at the bottom of the screen but outside the scroll
// region. Skip the LF. // region. Skip the LF.
logger.Info("Simulating LF outside scroll region") h.logf("Simulating LF outside scroll region")
if includeCR { if includeCR {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return false, err return false, err
@ -151,7 +168,7 @@ func (h *windowsAnsiEventHandler) executeLF() error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Info("Resetting cursor position for LF without CR") h.logf("Resetting cursor position for LF without CR")
if err := SetConsoleCursorPosition(h.fd, pos); err != nil { if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
return err return err
} }
@ -186,7 +203,7 @@ func (h *windowsAnsiEventHandler) Print(b byte) error {
func (h *windowsAnsiEventHandler) Execute(b byte) error { func (h *windowsAnsiEventHandler) Execute(b byte) error {
switch b { switch b {
case ansiterm.ANSI_TAB: case ansiterm.ANSI_TAB:
logger.Info("Execute(TAB)") h.logf("Execute(TAB)")
// Move to the next tab stop, but preserve auto-wrap if already set. // Move to the next tab stop, but preserve auto-wrap if already set.
if !h.wrapNext { if !h.wrapNext {
pos, info, err := h.getCurrentInfo() pos, info, err := h.getCurrentInfo()
@ -269,7 +286,7 @@ func (h *windowsAnsiEventHandler) CUU(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUU: [%v]", []string{strconv.Itoa(param)}) h.logf("CUU: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorVertical(-param) return h.moveCursorVertical(-param)
} }
@ -278,7 +295,7 @@ func (h *windowsAnsiEventHandler) CUD(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUD: [%v]", []string{strconv.Itoa(param)}) h.logf("CUD: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorVertical(param) return h.moveCursorVertical(param)
} }
@ -287,7 +304,7 @@ func (h *windowsAnsiEventHandler) CUF(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUF: [%v]", []string{strconv.Itoa(param)}) h.logf("CUF: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorHorizontal(param) return h.moveCursorHorizontal(param)
} }
@ -296,7 +313,7 @@ func (h *windowsAnsiEventHandler) CUB(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUB: [%v]", []string{strconv.Itoa(param)}) h.logf("CUB: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorHorizontal(-param) return h.moveCursorHorizontal(-param)
} }
@ -305,7 +322,7 @@ func (h *windowsAnsiEventHandler) CNL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CNL: [%v]", []string{strconv.Itoa(param)}) h.logf("CNL: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorLine(param) return h.moveCursorLine(param)
} }
@ -314,7 +331,7 @@ func (h *windowsAnsiEventHandler) CPL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CPL: [%v]", []string{strconv.Itoa(param)}) h.logf("CPL: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorLine(-param) return h.moveCursorLine(-param)
} }
@ -323,7 +340,7 @@ func (h *windowsAnsiEventHandler) CHA(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CHA: [%v]", []string{strconv.Itoa(param)}) h.logf("CHA: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.moveCursorColumn(param) return h.moveCursorColumn(param)
} }
@ -332,7 +349,7 @@ func (h *windowsAnsiEventHandler) VPA(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("VPA: [[%d]]", param) h.logf("VPA: [[%d]]", param)
h.clearWrap() h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
@ -348,7 +365,7 @@ func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("CUP: [[%d %d]]", row, col) h.logf("CUP: [[%d %d]]", row, col)
h.clearWrap() h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
@ -364,7 +381,7 @@ func (h *windowsAnsiEventHandler) HVP(row int, col int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("HVP: [[%d %d]]", row, col) h.logf("HVP: [[%d %d]]", row, col)
h.clearWrap() h.clearWrap()
return h.CUP(row, col) return h.CUP(row, col)
} }
@ -373,7 +390,7 @@ func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECTCEM: [%v]", []string{strconv.FormatBool(visible)}) h.logf("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
h.clearWrap() h.clearWrap()
return nil return nil
} }
@ -382,7 +399,7 @@ func (h *windowsAnsiEventHandler) DECOM(enable bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECOM: [%v]", []string{strconv.FormatBool(enable)}) h.logf("DECOM: [%v]", []string{strconv.FormatBool(enable)})
h.clearWrap() h.clearWrap()
h.originMode = enable h.originMode = enable
return h.CUP(1, 1) return h.CUP(1, 1)
@ -392,7 +409,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECCOLM: [%v]", []string{strconv.FormatBool(use132)}) h.logf("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
h.clearWrap() h.clearWrap()
if err := h.ED(2); err != nil { if err := h.ED(2); err != nil {
return err return err
@ -407,7 +424,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
} }
if info.Size.X < targetWidth { if info.Size.X < targetWidth {
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
logger.Info("set buffer failed:", err) h.logf("set buffer failed: %v", err)
return err return err
} }
} }
@ -415,12 +432,12 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
window.Left = 0 window.Left = 0
window.Right = targetWidth - 1 window.Right = targetWidth - 1
if err := SetConsoleWindowInfo(h.fd, true, window); err != nil { if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
logger.Info("set window failed:", err) h.logf("set window failed: %v", err)
return err return err
} }
if info.Size.X > targetWidth { if info.Size.X > targetWidth {
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
logger.Info("set buffer failed:", err) h.logf("set buffer failed: %v", err)
return err return err
} }
} }
@ -431,7 +448,7 @@ func (h *windowsAnsiEventHandler) ED(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("ED: [%v]", []string{strconv.Itoa(param)}) h.logf("ED: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
// [J -- Erases from the cursor to the end of the screen, including the cursor position. // [J -- Erases from the cursor to the end of the screen, including the cursor position.
@ -490,7 +507,7 @@ func (h *windowsAnsiEventHandler) EL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("EL: [%v]", strconv.Itoa(param)) h.logf("EL: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
// [K -- Erases from the cursor to the end of the line, including the cursor position. // [K -- Erases from the cursor to the end of the line, including the cursor position.
@ -531,7 +548,7 @@ func (h *windowsAnsiEventHandler) IL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("IL: [%v]", strconv.Itoa(param)) h.logf("IL: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
return h.insertLines(param) return h.insertLines(param)
} }
@ -540,7 +557,7 @@ func (h *windowsAnsiEventHandler) DL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DL: [%v]", strconv.Itoa(param)) h.logf("DL: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
return h.deleteLines(param) return h.deleteLines(param)
} }
@ -549,7 +566,7 @@ func (h *windowsAnsiEventHandler) ICH(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("ICH: [%v]", strconv.Itoa(param)) h.logf("ICH: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
return h.insertCharacters(param) return h.insertCharacters(param)
} }
@ -558,7 +575,7 @@ func (h *windowsAnsiEventHandler) DCH(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DCH: [%v]", strconv.Itoa(param)) h.logf("DCH: [%v]", strconv.Itoa(param))
h.clearWrap() h.clearWrap()
return h.deleteCharacters(param) return h.deleteCharacters(param)
} }
@ -572,7 +589,7 @@ func (h *windowsAnsiEventHandler) SGR(params []int) error {
strings = append(strings, strconv.Itoa(v)) strings = append(strings, strconv.Itoa(v))
} }
logger.Infof("SGR: [%v]", strings) h.logf("SGR: [%v]", strings)
if len(params) <= 0 { if len(params) <= 0 {
h.attributes = h.infoReset.Attributes h.attributes = h.infoReset.Attributes
@ -606,7 +623,7 @@ func (h *windowsAnsiEventHandler) SU(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("SU: [%v]", []string{strconv.Itoa(param)}) h.logf("SU: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.scrollUp(param) return h.scrollUp(param)
} }
@ -615,13 +632,13 @@ func (h *windowsAnsiEventHandler) SD(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("SD: [%v]", []string{strconv.Itoa(param)}) h.logf("SD: [%v]", []string{strconv.Itoa(param)})
h.clearWrap() h.clearWrap()
return h.scrollDown(param) return h.scrollDown(param)
} }
func (h *windowsAnsiEventHandler) DA(params []string) error { func (h *windowsAnsiEventHandler) DA(params []string) error {
logger.Infof("DA: [%v]", params) h.logf("DA: [%v]", params)
// DA cannot be implemented because it must send data on the VT100 input stream, // DA cannot be implemented because it must send data on the VT100 input stream,
// which is not available to go-ansiterm. // which is not available to go-ansiterm.
return nil return nil
@ -631,7 +648,7 @@ func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECSTBM: [%d, %d]", top, bottom) h.logf("DECSTBM: [%d, %d]", top, bottom)
// Windows is 0 indexed, Linux is 1 indexed // Windows is 0 indexed, Linux is 1 indexed
h.sr.top = int16(top - 1) h.sr.top = int16(top - 1)
@ -646,7 +663,7 @@ func (h *windowsAnsiEventHandler) RI() error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Info("RI: []") h.logf("RI: []")
h.clearWrap() h.clearWrap()
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
@ -663,21 +680,21 @@ func (h *windowsAnsiEventHandler) RI() error {
} }
func (h *windowsAnsiEventHandler) IND() error { func (h *windowsAnsiEventHandler) IND() error {
logger.Info("IND: []") h.logf("IND: []")
return h.executeLF() return h.executeLF()
} }
func (h *windowsAnsiEventHandler) Flush() error { func (h *windowsAnsiEventHandler) Flush() error {
h.curInfo = nil h.curInfo = nil
if h.buffer.Len() > 0 { if h.buffer.Len() > 0 {
logger.Infof("Flush: [%s]", h.buffer.Bytes()) h.logf("Flush: [%s]", h.buffer.Bytes())
if _, err := h.buffer.WriteTo(h.file); err != nil { if _, err := h.buffer.WriteTo(h.file); err != nil {
return err return err
} }
} }
if h.wrapNext && !h.drewMarginByte { if h.wrapNext && !h.drewMarginByte {
logger.Infof("Flush: drawing margin byte '%c'", h.marginByte) h.logf("Flush: drawing margin byte '%c'", h.marginByte)
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {