Browse Source

Detect arrow keys on windows (#3363)

* detect arrow keys on windows
* add some helpful comments
Jeffrey Morgan 1 year ago
parent
commit
913306f4fd
1 changed files with 14 additions and 39 deletions
  1. 14 39
      readline/term_windows.go

+ 14 - 39
readline/term_windows.go

@@ -1,29 +1,7 @@
 package readline
 
 import (
-	"syscall"
-	"unsafe"
-)
-
-const (
-	enableLineInput       = 2
-	enableWindowInput     = 8
-	enableMouseInput      = 16
-	enableInsertMode      = 32
-	enableQuickEditMode   = 64
-	enableExtendedFlags   = 128
-	enableProcessedOutput = 1
-	enableWrapAtEolOutput = 2
-	enableAutoPosition    = 256 // Cursor position is not affected by writing data to the console.
-	enableEchoInput       = 4   // Characters are written to the console as they're read.
-	enableProcessedInput  = 1   // Enables input processing (like recognizing Ctrl+C).
-)
-
-var kernel32 = syscall.NewLazyDLL("kernel32.dll")
-
-var (
-	procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
-	procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
+	"golang.org/x/sys/windows"
 )
 
 type State struct {
@@ -33,31 +11,28 @@ type State struct {
 // IsTerminal checks if the given file descriptor is associated with a terminal
 func IsTerminal(fd int) bool {
 	var st uint32
-	r, _, e := syscall.SyscallN(procGetConsoleMode.Addr(), uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
-	// if the call succeeds and doesn't produce an error, it's a terminal
-	return r != 0 && e == 0
+	err := windows.GetConsoleMode(windows.Handle(fd), &st)
+	return err == nil
 }
 
 func SetRawMode(fd int) (*State, error) {
 	var st uint32
-	// retrieve the current mode of the terminal
-	_, _, e := syscall.SyscallN(procGetConsoleMode.Addr(), uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
-	if e != 0 {
-		return nil, error(e)
+	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+		return nil, err
 	}
-	// modify the mode to set it to raw
-	raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
-	// apply the new mode to the terminal
-	_, _, e = syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(fd), uintptr(raw), 0)
-	if e != 0 {
-		return nil, error(e)
+
+	// this enables raw mode by turning off various flags in the console mode: https://pkg.go.dev/golang.org/x/sys/windows#pkg-constants
+	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
+
+	// turn on ENABLE_VIRTUAL_TERMINAL_INPUT to enable escape sequences
+	raw |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
+	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
+		return nil, err
 	}
-	// return the original state so that it can be restored later
 	return &State{st}, nil
 }
 
 func UnsetRawMode(fd int, state any) error {
 	s := state.(*State)
-	_, _, err := syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(fd), uintptr(s.mode), 0)
-	return err
+	return windows.SetConsoleMode(windows.Handle(fd), s.mode)
 }