github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/edit/tty/reader_windows.go (about)

     1  package tty
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/u-root/u-root/cmds/core/elvish/edit/ui"
    11  	"github.com/u-root/u-root/cmds/core/elvish/sys"
    12  	"golang.org/x/sys/windows"
    13  )
    14  
    15  // XXX Put here to make edit package build on Windows.
    16  const DefaultSeqTimeout = 10 * time.Millisecond
    17  
    18  type reader struct {
    19  	console   windows.Handle
    20  	eventChan chan Event
    21  
    22  	stopEvent   windows.Handle
    23  	stopChan    chan struct{}
    24  	stopAckChan chan struct{}
    25  }
    26  
    27  // NewReader creates a new Reader instance.
    28  func newReader(file *os.File) Reader {
    29  	console, err := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
    30  	if err != nil {
    31  		panic(fmt.Errorf("GetStdHandle(STD_INPUT_HANDLE): %v", err))
    32  	}
    33  	stopEvent, err := windows.CreateEvent(nil, 0, 0, nil)
    34  	if err != nil {
    35  		panic(fmt.Errorf("CreateEvent: %v", err))
    36  	}
    37  	return &reader{
    38  		console, make(chan Event), stopEvent, nil, nil}
    39  }
    40  
    41  func (r *reader) SetRaw(bool) {
    42  	// NOP on Windows.
    43  }
    44  
    45  func (r *reader) EventChan() <-chan Event {
    46  	return r.eventChan
    47  }
    48  
    49  func (r *reader) Start() {
    50  	r.stopChan = make(chan struct{})
    51  	r.stopAckChan = make(chan struct{})
    52  	go r.run()
    53  }
    54  
    55  var errNr0 = errors.New("ReadConsoleInput reads 0 input")
    56  
    57  func (r *reader) run() {
    58  	handles := []windows.Handle{r.console, r.stopEvent}
    59  	for {
    60  		triggered, _, err := sys.WaitForMultipleObjects(handles, false, sys.INFINITE)
    61  		if err != nil {
    62  			r.fatal(err)
    63  			return
    64  		}
    65  		if triggered == 1 {
    66  			<-r.stopChan
    67  			close(r.stopAckChan)
    68  			return
    69  		}
    70  
    71  		var buf [1]sys.InputRecord
    72  		nr, err := sys.ReadConsoleInput(r.console, buf[:])
    73  		if nr == 0 {
    74  			r.fatal(errNr0)
    75  			return
    76  		}
    77  		if err != nil {
    78  			r.fatal(err)
    79  			return
    80  		}
    81  		event := convertEvent(buf[0].GetEvent())
    82  		if event != nil {
    83  			r.send(event)
    84  		}
    85  	}
    86  }
    87  
    88  func (r *reader) nonFatal(err error) {
    89  	r.send(NonfatalErrorEvent{err})
    90  }
    91  
    92  func (r *reader) fatal(err error) {
    93  	if !r.send(FatalErrorEvent{err}) {
    94  		<-r.stopChan
    95  		close(r.stopAckChan)
    96  		r.resetStopEvent()
    97  	}
    98  }
    99  
   100  func (r *reader) send(event Event) (stopped bool) {
   101  	select {
   102  	case r.eventChan <- event:
   103  		return false
   104  	case <-r.stopChan:
   105  		close(r.stopAckChan)
   106  		r.resetStopEvent()
   107  		return true
   108  	}
   109  }
   110  
   111  func (r *reader) resetStopEvent() {
   112  	err := windows.ResetEvent(r.stopEvent)
   113  	if err != nil {
   114  		panic(err)
   115  	}
   116  }
   117  
   118  func (r *reader) Stop() {
   119  	err := windows.SetEvent(r.stopEvent)
   120  	if err != nil {
   121  		log.Println("SetEvent:", err)
   122  	}
   123  	close(r.stopChan)
   124  	<-r.stopAckChan
   125  }
   126  
   127  func (r *reader) Close() {
   128  	err := windows.CloseHandle(r.stopEvent)
   129  	if err != nil {
   130  		log.Println("Closing stopEvent handle for reader:", err)
   131  	}
   132  	close(r.eventChan)
   133  }
   134  
   135  // A subset of virtual key codes listed in
   136  // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
   137  var keyCodeToRune = map[uint16]rune{
   138  	0x08: ui.Backspace, 0x09: ui.Tab,
   139  	0x0d: ui.Enter,
   140  	0x1b: '\x1b',
   141  	0x20: ' ',
   142  	0x23: ui.End, 0x24: ui.Home,
   143  	0x25: ui.Left, 0x26: ui.Up, 0x27: ui.Right, 0x28: ui.Down,
   144  	0x2d: ui.Insert, 0x2e: ui.Delete,
   145  	/* 0x30 - 0x39: digits, same with ASCII */
   146  	/* 0x41 - 0x5a: letters, same with ASCII */
   147  	/* 0x60 - 0x6f: numpads; currently ignored */
   148  	0x70: ui.F1, 0x71: ui.F2, 0x72: ui.F3, 0x73: ui.F4, 0x74: ui.F5, 0x75: ui.F6,
   149  	0x76: ui.F7, 0x77: ui.F8, 0x78: ui.F9, 0x79: ui.F10, 0x7a: ui.F11, 0x7b: ui.F12,
   150  	/* 0x7c - 0x87: F13 - F24; currently ignored */
   151  	0xba: ';', 0xbb: '=', 0xbc: ',', 0xbd: '-', 0xbe: '.', 0xbf: '/', 0xc0: '`',
   152  	0xdb: '[', 0xdc: '\\', 0xdd: ']', 0xde: '\'',
   153  }
   154  
   155  // A subset of constants listed in
   156  // https://docs.microsoft.com/en-us/windows/console/key-event-record-str
   157  const (
   158  	leftAlt   = 0x02
   159  	leftCtrl  = 0x08
   160  	rightAlt  = 0x01
   161  	rightCtrl = 0x04
   162  	shift     = 0x10
   163  )
   164  
   165  // convertEvent converts the native sys.InputEvent type to a suitable Event
   166  // type. It returns nil if the event should be ignored.
   167  func convertEvent(event sys.InputEvent) Event {
   168  	switch event := event.(type) {
   169  	case *sys.KeyEvent:
   170  		if event.BKeyDown == 0 {
   171  			// Ignore keyup events.
   172  			return nil
   173  		}
   174  		r := rune(event.UChar[0]) + rune(event.UChar[1])<<8
   175  		if event.DwControlKeyState == 0 {
   176  			// No modifier
   177  			// TODO: Deal with surrogate pairs
   178  			if 0x20 <= r && r != 0x7f {
   179  				return KeyEvent(ui.Key{Rune: r})
   180  			}
   181  		} else if event.DwControlKeyState == shift {
   182  			// If only the shift is held down, we try and see if this is a
   183  			// non-functional key by looking if the rune generated is a
   184  			// printable ASCII character.
   185  			if 0x20 <= r && r < 0x7f {
   186  				return KeyEvent(ui.Key{Rune: r})
   187  			}
   188  		}
   189  		r = convertRune(event.WVirtualKeyCode)
   190  		if r == 0 {
   191  			return nil
   192  		}
   193  		mod := convertMod(event.DwControlKeyState)
   194  		return KeyEvent(ui.Key{Rune: r, Mod: mod})
   195  	//case *sys.MouseEvent:
   196  	//case *sys.WindowBufferSizeEvent:
   197  	default:
   198  		// Other events are ignored.
   199  		return nil
   200  	}
   201  }
   202  
   203  func convertRune(keyCode uint16) rune {
   204  	r, ok := keyCodeToRune[keyCode]
   205  	if ok {
   206  		return r
   207  	}
   208  	if '0' <= keyCode && keyCode <= '9' || 'A' <= keyCode && keyCode <= 'Z' {
   209  		return rune(keyCode)
   210  	}
   211  	return 0
   212  }
   213  
   214  func convertMod(state uint32) ui.Mod {
   215  	mod := ui.Mod(0)
   216  	if state&(leftAlt|rightAlt) != 0 {
   217  		mod |= ui.Alt
   218  	}
   219  	if state&(leftCtrl|rightCtrl) != 0 {
   220  		mod |= ui.Ctrl
   221  	}
   222  	if state&shift != 0 {
   223  		mod |= ui.Shift
   224  	}
   225  	return mod
   226  }