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  }