github.com/axw/llgo@v0.0.0-20160805011314-95b5fe4dca20/third_party/liner/input_windows.go (about) 1 package liner 2 3 import ( 4 "bufio" 5 "os" 6 "syscall" 7 "unsafe" 8 ) 9 10 var ( 11 kernel32 = syscall.NewLazyDLL("kernel32.dll") 12 13 procGetStdHandle = kernel32.NewProc("GetStdHandle") 14 procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW") 15 procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 16 procSetConsoleMode = kernel32.NewProc("SetConsoleMode") 17 procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") 18 procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 19 procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") 20 ) 21 22 // These names are from the Win32 api, so they use underscores (contrary to 23 // what golint suggests) 24 const ( 25 std_input_handle = uint32(-10 & 0xFFFFFFFF) 26 std_output_handle = uint32(-11 & 0xFFFFFFFF) 27 std_error_handle = uint32(-12 & 0xFFFFFFFF) 28 invalid_handle_value = ^uintptr(0) 29 ) 30 31 type inputMode uint32 32 33 // State represents an open terminal 34 type State struct { 35 commonState 36 handle syscall.Handle 37 hOut syscall.Handle 38 origMode inputMode 39 defaultMode inputMode 40 key interface{} 41 repeat uint16 42 } 43 44 const ( 45 enableEchoInput = 0x4 46 enableInsertMode = 0x20 47 enableLineInput = 0x2 48 enableMouseInput = 0x10 49 enableProcessedInput = 0x1 50 enableQuickEditMode = 0x40 51 enableWindowInput = 0x8 52 ) 53 54 // NewLiner initializes a new *State, and sets the terminal into raw mode. To 55 // restore the terminal to its previous state, call State.Close(). 56 func NewLiner() *State { 57 var s State 58 hIn, _, _ := procGetStdHandle.Call(uintptr(std_input_handle)) 59 s.handle = syscall.Handle(hIn) 60 hOut, _, _ := procGetStdHandle.Call(uintptr(std_output_handle)) 61 s.hOut = syscall.Handle(hOut) 62 63 s.terminalSupported = true 64 if m, err := TerminalMode(); err == nil { 65 s.origMode = m.(inputMode) 66 mode := s.origMode 67 mode &^= enableEchoInput 68 mode &^= enableInsertMode 69 mode &^= enableLineInput 70 mode &^= enableMouseInput 71 mode |= enableWindowInput 72 mode.ApplyMode() 73 } else { 74 s.inputRedirected = true 75 s.r = bufio.NewReader(os.Stdin) 76 } 77 78 s.getColumns() 79 s.outputRedirected = s.columns <= 0 80 81 return &s 82 } 83 84 // These names are from the Win32 api, so they use underscores (contrary to 85 // what golint suggests) 86 const ( 87 focus_event = 0x0010 88 key_event = 0x0001 89 menu_event = 0x0008 90 mouse_event = 0x0002 91 window_buffer_size_event = 0x0004 92 ) 93 94 type input_record struct { 95 eventType uint16 96 pad uint16 97 blob [16]byte 98 } 99 100 type key_event_record struct { 101 KeyDown int32 102 RepeatCount uint16 103 VirtualKeyCode uint16 104 VirtualScanCode uint16 105 Char int16 106 ControlKeyState uint32 107 } 108 109 // These names are from the Win32 api, so they use underscores (contrary to 110 // what golint suggests) 111 const ( 112 vk_tab = 0x09 113 vk_prior = 0x21 114 vk_next = 0x22 115 vk_end = 0x23 116 vk_home = 0x24 117 vk_left = 0x25 118 vk_up = 0x26 119 vk_right = 0x27 120 vk_down = 0x28 121 vk_insert = 0x2d 122 vk_delete = 0x2e 123 vk_f1 = 0x70 124 vk_f2 = 0x71 125 vk_f3 = 0x72 126 vk_f4 = 0x73 127 vk_f5 = 0x74 128 vk_f6 = 0x75 129 vk_f7 = 0x76 130 vk_f8 = 0x77 131 vk_f9 = 0x78 132 vk_f10 = 0x79 133 vk_f11 = 0x7a 134 vk_f12 = 0x7b 135 bKey = 0x42 136 fKey = 0x46 137 yKey = 0x59 138 ) 139 140 const ( 141 shiftPressed = 0x0010 142 leftAltPressed = 0x0002 143 leftCtrlPressed = 0x0008 144 rightAltPressed = 0x0001 145 rightCtrlPressed = 0x0004 146 147 modKeys = shiftPressed | leftAltPressed | rightAltPressed | leftCtrlPressed | rightCtrlPressed 148 ) 149 150 func (s *State) readNext() (interface{}, error) { 151 if s.repeat > 0 { 152 s.repeat-- 153 return s.key, nil 154 } 155 156 var input input_record 157 pbuf := uintptr(unsafe.Pointer(&input)) 158 var rv uint32 159 prv := uintptr(unsafe.Pointer(&rv)) 160 161 for { 162 ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), pbuf, 1, prv) 163 164 if ok == 0 { 165 return nil, err 166 } 167 168 if input.eventType == window_buffer_size_event { 169 xy := (*coord)(unsafe.Pointer(&input.blob[0])) 170 s.columns = int(xy.x) 171 return winch, nil 172 } 173 if input.eventType != key_event { 174 continue 175 } 176 ke := (*key_event_record)(unsafe.Pointer(&input.blob[0])) 177 if ke.KeyDown == 0 { 178 continue 179 } 180 181 if ke.VirtualKeyCode == vk_tab && ke.ControlKeyState&modKeys == shiftPressed { 182 s.key = shiftTab 183 } else if ke.VirtualKeyCode == bKey && (ke.ControlKeyState&modKeys == leftAltPressed || 184 ke.ControlKeyState&modKeys == rightAltPressed) { 185 s.key = altB 186 } else if ke.VirtualKeyCode == fKey && (ke.ControlKeyState&modKeys == leftAltPressed || 187 ke.ControlKeyState&modKeys == rightAltPressed) { 188 s.key = altF 189 } else if ke.VirtualKeyCode == yKey && (ke.ControlKeyState&modKeys == leftAltPressed || 190 ke.ControlKeyState&modKeys == rightAltPressed) { 191 s.key = altY 192 } else if ke.Char > 0 { 193 s.key = rune(ke.Char) 194 } else { 195 switch ke.VirtualKeyCode { 196 case vk_prior: 197 s.key = pageUp 198 case vk_next: 199 s.key = pageDown 200 case vk_end: 201 s.key = end 202 case vk_home: 203 s.key = home 204 case vk_left: 205 s.key = left 206 if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 { 207 if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) { 208 s.key = wordLeft 209 } 210 } 211 case vk_right: 212 s.key = right 213 if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 { 214 if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) { 215 s.key = wordRight 216 } 217 } 218 case vk_up: 219 s.key = up 220 case vk_down: 221 s.key = down 222 case vk_insert: 223 s.key = insert 224 case vk_delete: 225 s.key = del 226 case vk_f1: 227 s.key = f1 228 case vk_f2: 229 s.key = f2 230 case vk_f3: 231 s.key = f3 232 case vk_f4: 233 s.key = f4 234 case vk_f5: 235 s.key = f5 236 case vk_f6: 237 s.key = f6 238 case vk_f7: 239 s.key = f7 240 case vk_f8: 241 s.key = f8 242 case vk_f9: 243 s.key = f9 244 case vk_f10: 245 s.key = f10 246 case vk_f11: 247 s.key = f11 248 case vk_f12: 249 s.key = f12 250 default: 251 // Eat modifier keys 252 // TODO: return Action(Unknown) if the key isn't a 253 // modifier. 254 continue 255 } 256 } 257 258 if ke.RepeatCount > 1 { 259 s.repeat = ke.RepeatCount - 1 260 } 261 return s.key, nil 262 } 263 return unknown, nil 264 } 265 266 // Close returns the terminal to its previous mode 267 func (s *State) Close() error { 268 s.origMode.ApplyMode() 269 return nil 270 } 271 272 func (s *State) startPrompt() { 273 if m, err := TerminalMode(); err == nil { 274 s.defaultMode = m.(inputMode) 275 mode := s.defaultMode 276 mode &^= enableProcessedInput 277 mode.ApplyMode() 278 } 279 } 280 281 func (s *State) restartPrompt() { 282 } 283 284 func (s *State) stopPrompt() { 285 s.defaultMode.ApplyMode() 286 } 287 288 // TerminalSupported returns true because line editing is always 289 // supported on Windows. 290 func TerminalSupported() bool { 291 return true 292 } 293 294 func (mode inputMode) ApplyMode() error { 295 hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle)) 296 if hIn == invalid_handle_value || hIn == 0 { 297 return err 298 } 299 ok, _, err := procSetConsoleMode.Call(hIn, uintptr(mode)) 300 if ok != 0 { 301 err = nil 302 } 303 return err 304 } 305 306 // TerminalMode returns the current terminal input mode as an InputModeSetter. 307 // 308 // This function is provided for convenience, and should 309 // not be necessary for most users of liner. 310 func TerminalMode() (ModeApplier, error) { 311 var mode inputMode 312 hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle)) 313 if hIn == invalid_handle_value || hIn == 0 { 314 return nil, err 315 } 316 ok, _, err := procGetConsoleMode.Call(hIn, uintptr(unsafe.Pointer(&mode))) 317 if ok != 0 { 318 err = nil 319 } 320 return mode, err 321 }