github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/cli/term/reader_windows.go (about) 1 package term 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "sync" 9 10 "golang.org/x/sys/windows" 11 12 "github.com/markusbkk/elvish/pkg/sys/ewindows" 13 "github.com/markusbkk/elvish/pkg/ui" 14 ) 15 16 type reader struct { 17 console windows.Handle 18 stopEvent windows.Handle 19 // A mutex that is held during ReadEvent. 20 mutex sync.Mutex 21 } 22 23 // Creates a new Reader instance. 24 func newReader(file *os.File) Reader { 25 console, err := windows.GetStdHandle(windows.STD_INPUT_HANDLE) 26 if err != nil { 27 panic(fmt.Errorf("GetStdHandle(STD_INPUT_HANDLE): %v", err)) 28 } 29 stopEvent, err := windows.CreateEvent(nil, 0, 0, nil) 30 if err != nil { 31 panic(fmt.Errorf("CreateEvent: %v", err)) 32 } 33 return &reader{console: console, stopEvent: stopEvent} 34 } 35 36 func (r *reader) ReadEvent() (Event, error) { 37 r.mutex.Lock() 38 defer r.mutex.Unlock() 39 handles := []windows.Handle{r.console, r.stopEvent} 40 for { 41 triggered, _, err := ewindows.WaitForMultipleObjects(handles, false, ewindows.INFINITE) 42 if err != nil { 43 return nil, err 44 } 45 if triggered == 1 { 46 return nil, ErrStopped 47 } 48 49 var buf [1]ewindows.InputRecord 50 nr, err := ewindows.ReadConsoleInput(r.console, buf[:]) 51 if nr == 0 { 52 return nil, io.ErrNoProgress 53 } 54 if err != nil { 55 return nil, err 56 } 57 event := convertEvent(buf[0].GetEvent()) 58 if event != nil { 59 return event, nil 60 } 61 // Got an event that should be ignored; keep going. 62 } 63 } 64 65 func (r *reader) ReadRawEvent() (Event, error) { 66 return r.ReadEvent() 67 } 68 69 func (r *reader) Close() { 70 err := windows.SetEvent(r.stopEvent) 71 if err != nil { 72 log.Println("SetEvent:", err) 73 } 74 r.mutex.Lock() 75 r.mutex.Unlock() 76 err = windows.CloseHandle(r.stopEvent) 77 if err != nil { 78 log.Println("Closing stopEvent handle for reader:", err) 79 } 80 } 81 82 // A subset of virtual key codes listed in 83 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx 84 var keyCodeToRune = map[uint16]rune{ 85 0x08: ui.Backspace, 0x09: ui.Tab, 86 0x0d: ui.Enter, 87 0x1b: '\x1b', 88 0x20: ' ', 89 0x23: ui.End, 0x24: ui.Home, 90 0x25: ui.Left, 0x26: ui.Up, 0x27: ui.Right, 0x28: ui.Down, 91 0x2d: ui.Insert, 0x2e: ui.Delete, 92 /* 0x30 - 0x39: digits, same with ASCII */ 93 /* 0x41 - 0x5a: letters, same with ASCII */ 94 /* 0x60 - 0x6f: numpads; currently ignored */ 95 0x70: ui.F1, 0x71: ui.F2, 0x72: ui.F3, 0x73: ui.F4, 0x74: ui.F5, 0x75: ui.F6, 96 0x76: ui.F7, 0x77: ui.F8, 0x78: ui.F9, 0x79: ui.F10, 0x7a: ui.F11, 0x7b: ui.F12, 97 /* 0x7c - 0x87: F13 - F24; currently ignored */ 98 0xba: ';', 0xbb: '=', 0xbc: ',', 0xbd: '-', 0xbe: '.', 0xbf: '/', 0xc0: '`', 99 0xdb: '[', 0xdc: '\\', 0xdd: ']', 0xde: '\'', 100 } 101 102 // A subset of constants listed in 103 // https://docs.microsoft.com/en-us/windows/console/key-event-record-str 104 const ( 105 leftAlt = 0x02 106 leftCtrl = 0x08 107 rightAlt = 0x01 108 rightCtrl = 0x04 109 shift = 0x10 110 ) 111 112 // Converts the native ewindows.InputEvent type to a suitable Event type. It returns 113 // nil if the event should be ignored. 114 func convertEvent(event ewindows.InputEvent) Event { 115 switch event := event.(type) { 116 case *ewindows.KeyEvent: 117 if event.BKeyDown == 0 { 118 // Ignore keyup events. 119 return nil 120 } 121 r := rune(event.UChar[0]) + rune(event.UChar[1])<<8 122 filteredMod := event.DwControlKeyState & (leftAlt | leftCtrl | rightAlt | rightCtrl | shift) 123 if r >= 0x20 && r != 0x7f { 124 // This key inputs a character. The flags present in 125 // DwControlKeyState might indicate modifier keys that are needed to 126 // input this character (e.g. the Shift key when inputting 'A'), or 127 // modifier keys that are pressed in addition (e.g. the Alt key when 128 // pressing Alt-A). There doesn't seem to be an easy way to tell 129 // which is the case, so we rely on heuristics derived from 130 // real-world observations. 131 if filteredMod == 0 { 132 // TODO: Handle surrogate pairs 133 return KeyEvent(ui.Key{Rune: r}) 134 } else if filteredMod == shift { 135 // A lone Shift seems to be always part of the character. 136 return KeyEvent(ui.Key{Rune: r}) 137 } else if filteredMod == leftCtrl|rightAlt || filteredMod == leftCtrl|rightAlt|shift { 138 // The combination leftCtrl|rightAlt is used to represent AltGr. 139 // Furthermore, when the actual left Ctrl and right Alt are used 140 // together, the UChar field seems to be always 0; so if we are 141 // here, we can actually be sure that it's AltGr. 142 // 143 // Some characters require AltGr+Shift to input, such as the 144 // upper-case sharp S on a German keyboard. 145 return KeyEvent(ui.Key{Rune: r}) 146 } 147 } 148 mod := convertMod(filteredMod) 149 if mod == 0 && event.WVirtualKeyCode == 0x1b { 150 // Special case: Normalize 0x1b to Ctrl-[. 151 // 152 // TODO(xiaq): This is Unix-centric. Maybe the normalized form 153 // should be Escape. 154 return KeyEvent(ui.Key{Rune: '[', Mod: ui.Ctrl}) 155 } 156 r = convertRune(event.WVirtualKeyCode, mod) 157 if r == 0 { 158 return nil 159 } 160 return KeyEvent(ui.Key{Rune: r, Mod: mod}) 161 default: 162 // Other events are ignored. 163 return nil 164 } 165 } 166 167 func convertRune(keyCode uint16, mod ui.Mod) rune { 168 r, ok := keyCodeToRune[keyCode] 169 if ok { 170 return r 171 } 172 if '0' <= keyCode && keyCode <= '9' { 173 return rune(keyCode) 174 } 175 if 'A' <= keyCode && keyCode <= 'Z' { 176 // If Ctrl is involved, emulate UNIX's convention and use upper case; 177 // otherwise use lower case. 178 // 179 // TODO(xiaq): This is quite Unix-centric. Maybe we should make the 180 // base rune case-insensitive when there are modifiers involved. 181 if mod&ui.Ctrl != 0 { 182 return rune(keyCode) 183 } 184 return rune(keyCode - 'A' + 'a') 185 } 186 return 0 187 } 188 189 func convertMod(state uint32) ui.Mod { 190 mod := ui.Mod(0) 191 if state&(leftAlt|rightAlt) != 0 { 192 mod |= ui.Alt 193 } 194 if state&(leftCtrl|rightCtrl) != 0 { 195 mod |= ui.Ctrl 196 } 197 if state&shift != 0 { 198 mod |= ui.Shift 199 } 200 return mod 201 }