github.com/yamamoto-febc/docker@v1.9.0/pkg/term/windows/ansi_reader.go (about)

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