...

Source file src/github.com/moby/term/windows/ansi_reader.go

Documentation: github.com/moby/term/windows

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

View as plain text