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 }