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  }