Detect arrow keys on windows (#3363)
* detect arrow keys on windows * add some helpful comments
This commit is contained in:
parent
f5ca7f8c8e
commit
913306f4fd
1 changed files with 14 additions and 39 deletions
|
@ -1,29 +1,7 @@
|
||||||
package readline
|
package readline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"golang.org/x/sys/windows"
|
||||||
"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")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
|
@ -33,31 +11,28 @@ type State struct {
|
||||||
// IsTerminal checks if the given file descriptor is associated with a terminal
|
// IsTerminal checks if the given file descriptor is associated with a terminal
|
||||||
func IsTerminal(fd int) bool {
|
func IsTerminal(fd int) bool {
|
||||||
var st uint32
|
var st uint32
|
||||||
r, _, e := syscall.SyscallN(procGetConsoleMode.Addr(), uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
err := windows.GetConsoleMode(windows.Handle(fd), &st)
|
||||||
// if the call succeeds and doesn't produce an error, it's a terminal
|
return err == nil
|
||||||
return r != 0 && e == 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetRawMode(fd int) (*State, error) {
|
func SetRawMode(fd int) (*State, error) {
|
||||||
var st uint32
|
var st uint32
|
||||||
// retrieve the current mode of the terminal
|
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||||
_, _, e := syscall.SyscallN(procGetConsoleMode.Addr(), uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
return nil, err
|
||||||
if e != 0 {
|
|
||||||
return nil, error(e)
|
|
||||||
}
|
}
|
||||||
// modify the mode to set it to raw
|
|
||||||
raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
|
// this enables raw mode by turning off various flags in the console mode: https://pkg.go.dev/golang.org/x/sys/windows#pkg-constants
|
||||||
// apply the new mode to the terminal
|
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||||
_, _, e = syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(fd), uintptr(raw), 0)
|
|
||||||
if e != 0 {
|
// turn on ENABLE_VIRTUAL_TERMINAL_INPUT to enable escape sequences
|
||||||
return nil, error(e)
|
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
|
return &State{st}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnsetRawMode(fd int, state any) error {
|
func UnsetRawMode(fd int, state any) error {
|
||||||
s := state.(*State)
|
s := state.(*State)
|
||||||
_, _, err := syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(fd), uintptr(s.mode), 0)
|
return windows.SetConsoleMode(windows.Handle(fd), s.mode)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue