github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/cli/term/reader_windows.go (about) 1 package term 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "sync" 9 "time" 10 11 "golang.org/x/sys/windows" 12 "src.elv.sh/pkg/sys" 13 "src.elv.sh/pkg/ui" 14 ) 15 16 // TODO(xiaq): Put here to make edit package build on Windows. Refactor so 17 // that isn't needed? 18 const DefaultSeqTimeout = 10 * time.Millisecond 19 20 type reader struct { 21 console windows.Handle 22 stopEvent windows.Handle 23 // A mutex that is held during ReadEvent. 24 mutex sync.Mutex 25 } 26 27 // 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{console: console, stopEvent: stopEvent} 38 } 39 40 func (r *reader) ReadEvent() (Event, error) { 41 r.mutex.Lock() 42 defer r.mutex.Unlock() 43 handles := []windows.Handle{r.console, r.stopEvent} 44 for { 45 triggered, _, err := sys.WaitForMultipleObjects(handles, false, sys.INFINITE) 46 if err != nil { 47 return nil, err 48 } 49 if triggered == 1 { 50 return nil, ErrStopped 51 } 52 53 var buf [1]sys.InputRecord 54 nr, err := sys.ReadConsoleInput(r.console, buf[:]) 55 if nr == 0 { 56 return nil, io.ErrNoProgress 57 } 58 if err != nil { 59 return nil, err 60 } 61 event := convertEvent(buf[0].GetEvent()) 62 if event != nil { 63 return event, nil 64 } 65 // Got an event that should be ignored; keep going. 66 } 67 } 68 69 func (r *reader) ReadRawEvent() (Event, error) { 70 return r.ReadEvent() 71 } 72 73 func (r *reader) Close() { 74 err := windows.SetEvent(r.stopEvent) 75 if err != nil { 76 log.Println("SetEvent:", err) 77 } 78 r.mutex.Lock() 79 r.mutex.Unlock() 80 err = windows.CloseHandle(r.stopEvent) 81 if err != nil { 82 log.Println("Closing stopEvent handle for reader:", err) 83 } 84 } 85 86 // A subset of virtual key codes listed in 87 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx 88 var keyCodeToRune = map[uint16]rune{ 89 0x08: ui.Backspace, 0x09: ui.Tab, 90 0x0d: ui.Enter, 91 0x1b: '\x1b', 92 0x20: ' ', 93 0x23: ui.End, 0x24: ui.Home, 94 0x25: ui.Left, 0x26: ui.Up, 0x27: ui.Right, 0x28: ui.Down, 95 0x2d: ui.Insert, 0x2e: ui.Delete, 96 /* 0x30 - 0x39: digits, same with ASCII */ 97 /* 0x41 - 0x5a: letters, same with ASCII */ 98 /* 0x60 - 0x6f: numpads; currently ignored */ 99 0x70: ui.F1, 0x71: ui.F2, 0x72: ui.F3, 0x73: ui.F4, 0x74: ui.F5, 0x75: ui.F6, 100 0x76: ui.F7, 0x77: ui.F8, 0x78: ui.F9, 0x79: ui.F10, 0x7a: ui.F11, 0x7b: ui.F12, 101 /* 0x7c - 0x87: F13 - F24; currently ignored */ 102 0xba: ';', 0xbb: '=', 0xbc: ',', 0xbd: '-', 0xbe: '.', 0xbf: '/', 0xc0: '`', 103 0xdb: '[', 0xdc: '\\', 0xdd: ']', 0xde: '\'', 104 } 105 106 // A subset of constants listed in 107 // https://docs.microsoft.com/en-us/windows/console/key-event-record-str 108 const ( 109 leftAlt = 0x02 110 leftCtrl = 0x08 111 rightAlt = 0x01 112 rightCtrl = 0x04 113 shift = 0x10 114 ) 115 116 // convertEvent converts the native sys.InputEvent type to a suitable Event 117 // type. It returns nil if the event should be ignored. 118 func convertEvent(event sys.InputEvent) Event { 119 switch event := event.(type) { 120 case *sys.KeyEvent: 121 if event.BKeyDown == 0 { 122 // Ignore keyup events. 123 return nil 124 } 125 r := rune(event.UChar[0]) + rune(event.UChar[1])<<8 126 filteredMod := event.DwControlKeyState & (leftAlt | leftCtrl | rightAlt | rightCtrl | shift) 127 if filteredMod == 0 { 128 // No modifier 129 // TODO: Deal with surrogate pairs 130 if 0x20 <= r && r != 0x7f { 131 return KeyEvent(ui.Key{Rune: r}) 132 } 133 } else if filteredMod == shift { 134 // If only the shift is held down, we try and see if this is a 135 // non-functional key by looking if the rune generated is a 136 // printable ASCII character. 137 if 0x20 <= r && r < 0x7f { 138 return KeyEvent(ui.Key{Rune: r}) 139 } 140 } 141 mod := convertMod(filteredMod) 142 if mod == 0 && event.WVirtualKeyCode == 0x1b { 143 // Special case: Normalize 0x1b to Ctrl-[. 144 // 145 // TODO(xiaq): This is Unix-centric. Maybe the normalized form 146 // should be Escape. 147 return KeyEvent(ui.Key{Rune: '[', Mod: ui.Ctrl}) 148 } 149 r = convertRune(event.WVirtualKeyCode, mod) 150 if r == 0 { 151 return nil 152 } 153 return KeyEvent(ui.Key{Rune: r, Mod: mod}) 154 //case *sys.MouseEvent: 155 //case *sys.WindowBufferSizeEvent: 156 default: 157 // Other events are ignored. 158 return nil 159 } 160 } 161 162 func convertRune(keyCode uint16, mod ui.Mod) rune { 163 r, ok := keyCodeToRune[keyCode] 164 if ok { 165 return r 166 } 167 if '0' <= keyCode && keyCode <= '9' { 168 return rune(keyCode) 169 } 170 if 'A' <= keyCode && keyCode <= 'Z' { 171 // If Ctrl is involved, emulate UNIX's convention and use upper case; 172 // otherwise use lower case. 173 // 174 // TODO(xiaq): This is quite Unix-centric. Maybe we should make the 175 // base rune case-insensitive when there are modifiers involved. 176 if mod&ui.Ctrl != 0 { 177 return rune(keyCode) 178 } 179 return rune(keyCode - 'A' + 'a') 180 } 181 return 0 182 } 183 184 func convertMod(state uint32) ui.Mod { 185 mod := ui.Mod(0) 186 if state&(leftAlt|rightAlt) != 0 { 187 mod |= ui.Alt 188 } 189 if state&(leftCtrl|rightCtrl) != 0 { 190 mod |= ui.Ctrl 191 } 192 if state&shift != 0 { 193 mod |= ui.Shift 194 } 195 return mod 196 }