github.com/olljanat/moby@v1.13.1/pkg/term/windows/ansi_reader.go (about)

     1  // +build windows
     2  
     3  package windows
     4  
     5  import (
     6  	"bytes"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"strings"
    12  	"unsafe"
    13  
    14  	ansiterm "github.com/Azure/go-ansiterm"
    15  	"github.com/Azure/go-ansiterm/winterm"
    16  )
    17  
    18  const (
    19  	escapeSequence = ansiterm.KEY_ESC_CSI
    20  )
    21  
    22  // ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation.
    23  type ansiReader struct {
    24  	file     *os.File
    25  	fd       uintptr
    26  	buffer   []byte
    27  	cbBuffer int
    28  	command  []byte
    29  }
    30  
    31  // NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
    32  // Windows console input handle.
    33  func NewAnsiReader(nFile int) io.ReadCloser {
    34  	initLogger()
    35  	file, fd := winterm.GetStdFile(nFile)
    36  	return &ansiReader{
    37  		file:    file,
    38  		fd:      fd,
    39  		command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
    40  		buffer:  make([]byte, 0),
    41  	}
    42  }
    43  
    44  // Close closes the wrapped file.
    45  func (ar *ansiReader) Close() (err error) {
    46  	return ar.file.Close()
    47  }
    48  
    49  // Fd returns the file descriptor of the wrapped file.
    50  func (ar *ansiReader) Fd() uintptr {
    51  	return ar.fd
    52  }
    53  
    54  // Read reads up to len(p) bytes of translated input events into p.
    55  func (ar *ansiReader) Read(p []byte) (int, error) {
    56  	if len(p) == 0 {
    57  		return 0, nil
    58  	}
    59  
    60  	// Previously read bytes exist, read as much as we can and return
    61  	if len(ar.buffer) > 0 {
    62  		logger.Debugf("Reading previously cached bytes")
    63  
    64  		originalLength := len(ar.buffer)
    65  		copiedLength := copy(p, ar.buffer)
    66  
    67  		if copiedLength == originalLength {
    68  			ar.buffer = make([]byte, 0, len(p))
    69  		} else {
    70  			ar.buffer = ar.buffer[copiedLength:]
    71  		}
    72  
    73  		logger.Debugf("Read from cache p[%d]: % x", copiedLength, p)
    74  		return copiedLength, nil
    75  	}
    76  
    77  	// Read and translate key events
    78  	events, err := readInputEvents(ar.fd, len(p))
    79  	if err != nil {
    80  		return 0, err
    81  	} else if len(events) == 0 {
    82  		logger.Debug("No input events detected")
    83  		return 0, nil
    84  	}
    85  
    86  	keyBytes := translateKeyEvents(events, []byte(escapeSequence))
    87  
    88  	// Save excess bytes and right-size keyBytes
    89  	if len(keyBytes) > len(p) {
    90  		logger.Debugf("Received %d keyBytes, only room for %d bytes", len(keyBytes), len(p))
    91  		ar.buffer = keyBytes[len(p):]
    92  		keyBytes = keyBytes[:len(p)]
    93  	} else if len(keyBytes) == 0 {
    94  		logger.Debug("No key bytes returned from the translator")
    95  		return 0, nil
    96  	}
    97  
    98  	copiedLength := copy(p, keyBytes)
    99  	if copiedLength != len(keyBytes) {
   100  		return 0, errors.New("unexpected copy length encountered")
   101  	}
   102  
   103  	logger.Debugf("Read        p[%d]: % x", copiedLength, p)
   104  	logger.Debugf("Read keyBytes[%d]: % x", copiedLength, keyBytes)
   105  	return copiedLength, nil
   106  }
   107  
   108  // readInputEvents polls until at least one event is available.
   109  func readInputEvents(fd uintptr, maxBytes int) ([]winterm.INPUT_RECORD, error) {
   110  	// Determine the maximum number of records to retrieve
   111  	// -- Cast around the type system to obtain the size of a single INPUT_RECORD.
   112  	//    unsafe.Sizeof requires an expression vs. a type-reference; the casting
   113  	//    tricks the type system into believing it has such an expression.
   114  	recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes)))))
   115  	countRecords := maxBytes / recordSize
   116  	if countRecords > ansiterm.MAX_INPUT_EVENTS {
   117  		countRecords = ansiterm.MAX_INPUT_EVENTS
   118  	} else if countRecords == 0 {
   119  		countRecords = 1
   120  	}
   121  	logger.Debugf("[windows] readInputEvents: Reading %v records (buffer size %v, record size %v)", countRecords, maxBytes, recordSize)
   122  
   123  	// Wait for and read input events
   124  	events := make([]winterm.INPUT_RECORD, countRecords)
   125  	nEvents := uint32(0)
   126  	eventsExist, err := winterm.WaitForSingleObject(fd, winterm.WAIT_INFINITE)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	if eventsExist {
   132  		err = winterm.ReadConsoleInput(fd, events, &nEvents)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  	}
   137  
   138  	// Return a slice restricted to the number of returned records
   139  	logger.Debugf("[windows] readInputEvents: Read %v events", nEvents)
   140  	return events[:nEvents], nil
   141  }
   142  
   143  // KeyEvent Translation Helpers
   144  
   145  var arrowKeyMapPrefix = map[uint16]string{
   146  	winterm.VK_UP:    "%s%sA",
   147  	winterm.VK_DOWN:  "%s%sB",
   148  	winterm.VK_RIGHT: "%s%sC",
   149  	winterm.VK_LEFT:  "%s%sD",
   150  }
   151  
   152  var keyMapPrefix = map[uint16]string{
   153  	winterm.VK_UP:     "\x1B[%sA",
   154  	winterm.VK_DOWN:   "\x1B[%sB",
   155  	winterm.VK_RIGHT:  "\x1B[%sC",
   156  	winterm.VK_LEFT:   "\x1B[%sD",
   157  	winterm.VK_HOME:   "\x1B[1%s~", // showkey shows ^[[1
   158  	winterm.VK_END:    "\x1B[4%s~", // showkey shows ^[[4
   159  	winterm.VK_INSERT: "\x1B[2%s~",
   160  	winterm.VK_DELETE: "\x1B[3%s~",
   161  	winterm.VK_PRIOR:  "\x1B[5%s~",
   162  	winterm.VK_NEXT:   "\x1B[6%s~",
   163  	winterm.VK_F1:     "",
   164  	winterm.VK_F2:     "",
   165  	winterm.VK_F3:     "\x1B[13%s~",
   166  	winterm.VK_F4:     "\x1B[14%s~",
   167  	winterm.VK_F5:     "\x1B[15%s~",
   168  	winterm.VK_F6:     "\x1B[17%s~",
   169  	winterm.VK_F7:     "\x1B[18%s~",
   170  	winterm.VK_F8:     "\x1B[19%s~",
   171  	winterm.VK_F9:     "\x1B[20%s~",
   172  	winterm.VK_F10:    "\x1B[21%s~",
   173  	winterm.VK_F11:    "\x1B[23%s~",
   174  	winterm.VK_F12:    "\x1B[24%s~",
   175  }
   176  
   177  // translateKeyEvents converts the input events into the appropriate ANSI string.
   178  func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte {
   179  	var buffer bytes.Buffer
   180  	for _, event := range events {
   181  		if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 {
   182  			buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence))
   183  		}
   184  	}
   185  
   186  	return buffer.Bytes()
   187  }
   188  
   189  // keyToString maps the given input event record to the corresponding string.
   190  func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string {
   191  	if keyEvent.UnicodeChar == 0 {
   192  		return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence)
   193  	}
   194  
   195  	_, alt, control := getControlKeys(keyEvent.ControlKeyState)
   196  	if control {
   197  		// TODO(azlinux): Implement following control sequences
   198  		// <Ctrl>-D  Signals the end of input from the keyboard; also exits current shell.
   199  		// <Ctrl>-H  Deletes the first character to the left of the cursor. Also called the ERASE key.
   200  		// <Ctrl>-Q  Restarts printing after it has been stopped with <Ctrl>-s.
   201  		// <Ctrl>-S  Suspends printing on the screen (does not stop the program).
   202  		// <Ctrl>-U  Deletes all characters on the current line. Also called the KILL key.
   203  		// <Ctrl>-E  Quits current command and creates a core
   204  
   205  	}
   206  
   207  	// <Alt>+Key generates ESC N Key
   208  	if !control && alt {
   209  		return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar))
   210  	}
   211  
   212  	return string(keyEvent.UnicodeChar)
   213  }
   214  
   215  // formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string.
   216  func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string {
   217  	shift, alt, control := getControlKeys(controlState)
   218  	modifier := getControlKeysModifier(shift, alt, control)
   219  
   220  	if format, ok := arrowKeyMapPrefix[key]; ok {
   221  		return fmt.Sprintf(format, escapeSequence, modifier)
   222  	}
   223  
   224  	if format, ok := keyMapPrefix[key]; ok {
   225  		return fmt.Sprintf(format, modifier)
   226  	}
   227  
   228  	return ""
   229  }
   230  
   231  // getControlKeys extracts the shift, alt, and ctrl key states.
   232  func getControlKeys(controlState uint32) (shift, alt, control bool) {
   233  	shift = 0 != (controlState & winterm.SHIFT_PRESSED)
   234  	alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED))
   235  	control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED))
   236  	return shift, alt, control
   237  }
   238  
   239  // getControlKeysModifier returns the ANSI modifier for the given combination of control keys.
   240  func getControlKeysModifier(shift, alt, control bool) string {
   241  	if shift && alt && control {
   242  		return ansiterm.KEY_CONTROL_PARAM_8
   243  	}
   244  	if alt && control {
   245  		return ansiterm.KEY_CONTROL_PARAM_7
   246  	}
   247  	if shift && control {
   248  		return ansiterm.KEY_CONTROL_PARAM_6
   249  	}
   250  	if control {
   251  		return ansiterm.KEY_CONTROL_PARAM_5
   252  	}
   253  	if shift && alt {
   254  		return ansiterm.KEY_CONTROL_PARAM_4
   255  	}
   256  	if alt {
   257  		return ansiterm.KEY_CONTROL_PARAM_3
   258  	}
   259  	if shift {
   260  		return ansiterm.KEY_CONTROL_PARAM_2
   261  	}
   262  	return ""
   263  }