github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/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 yKey = 0x59 136 ) 137 138 const ( 139 shiftPressed = 0x0010 140 leftAltPressed = 0x0002 141 leftCtrlPressed = 0x0008 142 rightAltPressed = 0x0001 143 rightCtrlPressed = 0x0004 144 145 modKeys = shiftPressed | leftAltPressed | rightAltPressed | leftCtrlPressed | rightCtrlPressed 146 ) 147 148 func (s *State) readNext() (interface{}, error) { 149 if s.repeat > 0 { 150 s.repeat-- 151 return s.key, nil 152 } 153 154 var input input_record 155 pbuf := uintptr(unsafe.Pointer(&input)) 156 var rv uint32 157 prv := uintptr(unsafe.Pointer(&rv)) 158 159 for { 160 ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), pbuf, 1, prv) 161 162 if ok == 0 { 163 return nil, err 164 } 165 166 if input.eventType == window_buffer_size_event { 167 xy := (*coord)(unsafe.Pointer(&input.blob[0])) 168 s.columns = int(xy.x) 169 return winch, nil 170 } 171 if input.eventType != key_event { 172 continue 173 } 174 ke := (*key_event_record)(unsafe.Pointer(&input.blob[0])) 175 if ke.KeyDown == 0 { 176 continue 177 } 178 179 if ke.VirtualKeyCode == vk_tab && ke.ControlKeyState&modKeys == shiftPressed { 180 s.key = shiftTab 181 } else if ke.VirtualKeyCode == yKey && (ke.ControlKeyState&modKeys == leftAltPressed || 182 ke.ControlKeyState&modKeys == rightAltPressed) { 183 s.key = altY 184 } else if ke.Char > 0 { 185 s.key = rune(ke.Char) 186 } else { 187 switch ke.VirtualKeyCode { 188 case vk_prior: 189 s.key = pageUp 190 case vk_next: 191 s.key = pageDown 192 case vk_end: 193 s.key = end 194 case vk_home: 195 s.key = home 196 case vk_left: 197 s.key = left 198 if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 { 199 if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) { 200 s.key = wordLeft 201 } 202 } 203 case vk_right: 204 s.key = right 205 if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 { 206 if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) { 207 s.key = wordRight 208 } 209 } 210 case vk_up: 211 s.key = up 212 case vk_down: 213 s.key = down 214 case vk_insert: 215 s.key = insert 216 case vk_delete: 217 s.key = del 218 case vk_f1: 219 s.key = f1 220 case vk_f2: 221 s.key = f2 222 case vk_f3: 223 s.key = f3 224 case vk_f4: 225 s.key = f4 226 case vk_f5: 227 s.key = f5 228 case vk_f6: 229 s.key = f6 230 case vk_f7: 231 s.key = f7 232 case vk_f8: 233 s.key = f8 234 case vk_f9: 235 s.key = f9 236 case vk_f10: 237 s.key = f10 238 case vk_f11: 239 s.key = f11 240 case vk_f12: 241 s.key = f12 242 default: 243 // Eat modifier keys 244 // TODO: return Action(Unknown) if the key isn't a 245 // modifier. 246 continue 247 } 248 } 249 250 if ke.RepeatCount > 1 { 251 s.repeat = ke.RepeatCount - 1 252 } 253 return s.key, nil 254 } 255 return unknown, nil 256 } 257 258 // Close returns the terminal to its previous mode 259 func (s *State) Close() error { 260 s.origMode.ApplyMode() 261 return nil 262 } 263 264 func (s *State) startPrompt() { 265 if m, err := TerminalMode(); err == nil { 266 s.defaultMode = m.(inputMode) 267 mode := s.defaultMode 268 mode &^= enableProcessedInput 269 mode.ApplyMode() 270 } 271 } 272 273 func (s *State) restartPrompt() { 274 } 275 276 func (s *State) stopPrompt() { 277 s.defaultMode.ApplyMode() 278 } 279 280 // TerminalSupported returns true because line editing is always 281 // supported on Windows. 282 func TerminalSupported() bool { 283 return true 284 } 285 286 func (mode inputMode) ApplyMode() error { 287 hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle)) 288 if hIn == invalid_handle_value || hIn == 0 { 289 return err 290 } 291 ok, _, err := procSetConsoleMode.Call(hIn, uintptr(mode)) 292 if ok != 0 { 293 err = nil 294 } 295 return err 296 } 297 298 // TerminalMode returns the current terminal input mode as an InputModeSetter. 299 // 300 // This function is provided for convenience, and should 301 // not be necessary for most users of liner. 302 func TerminalMode() (ModeApplier, error) { 303 var mode inputMode 304 hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle)) 305 if hIn == invalid_handle_value || hIn == 0 { 306 return nil, err 307 } 308 ok, _, err := procGetConsoleMode.Call(hIn, uintptr(unsafe.Pointer(&mode))) 309 if ok != 0 { 310 err = nil 311 } 312 return mode, err 313 }