Skip to content

Instantly share code, notes, and snippets.

@zellyn
Created March 3, 2016 19:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zellyn/14935c93efc64e4d4162 to your computer and use it in GitHub Desktop.
Save zellyn/14935c93efc64e4d4162 to your computer and use it in GitHub Desktop.
evil keypress
I cobbled together a (unix-specific, syscall-using) solution based on the
stack overflow answer to the same question in python, and a previous post
on this list. References inline.
Note: this seems to work, but I haven't taken the time to really understand
what I'm doing, and I welcome any suggestions or fixes.
// https://groups.google.com/d/msg/golang-nuts/8o9fxPaeFu8/uSFYfobL5EgJ
// http://go.pastie.org/813153
func getTermios() (result syscall.Termios, err error) {
r1, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdin),
syscall.TCGETS, uintptr(unsafe.Pointer(&result)))
if errno != 0 {
return result, os.NewSyscallError("SYS_IOCTL", errno)
}
if r1 != 0 {
return result, fmt.Errorf("Error: expected first syscall result to be 0,
got %d", r1)
}
return result, nil
}
func setTermios(t syscall.Termios) error {
r1, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdin),
syscall.TCSETS, uintptr(unsafe.Pointer(&t)))
if errno != 0 {
return os.NewSyscallError("SYS_IOCTL", errno)
}
if r1 != 0 {
return fmt.Errorf("Error: expected first syscall result to be 0, got %d",
r1)
}
return nil
}
func getFileStatusFlags() (int32, error) {
r1, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(syscall.Stdin),
syscall.F_GETFL, 0)
if errno != 0 {
return 0, os.NewSyscallError("SYS_FCNTL", errno)
}
r := int32(r1)
if r < 0 {
return 0, fmt.Errorf("Error: expected first syscall result to be >= 0, got
%d", r)
}
return r, nil
}
func setFileStatusFlags(f int32) error {
r1, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(syscall.Stdin),
syscall.F_SETFL, uintptr(f))
if errno != 0 {
return os.NewSyscallError("SYS_FCNTL", errno)
}
if r1 != 0 {
return fmt.Errorf("Error: expected first syscall result to be 0, got %d",
r1)
}
return nil
}
// http://stackoverflow.com/a/6599441/23582
func readSingleKeypress() (byte, error) {
oldFl, err := getFileStatusFlags()
if err != nil {
return 0, err
}
oldTermios, err := getTermios()
if err != nil {
return 0, err
}
defer setFileStatusFlags(oldFl)
defer setTermios(oldTermios)
newFl, newTermios := oldFl, oldTermios
newTermios.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK |
syscall.ISTRIP)
newTermios.Iflag &^= (syscall.INLCR | syscall.IGNCR | syscall.ICRNL |
syscall.IXON)
newTermios.Oflag &^= syscall.OPOST
newTermios.Cflag &^= (syscall.CSIZE | syscall.PARENB)
newTermios.Cflag |= syscall.CS8
newTermios.Lflag &^= (syscall.ECHONL | syscall.ECHO | syscall.ICANON |
syscall.ISIG | syscall.IEXTEN)
newTermios.Cc[syscall.VMIN] = 1
newTermios.Cc[syscall.VTIME] = 0
if err = setTermios(newTermios); err != nil {
return 0, err
}
newFl &^= syscall.O_NONBLOCK
if err = setFileStatusFlags(newFl); err != nil {
return 0, err
}
keys := []byte{0}
n, err := syscall.Read(syscall.Stdin, keys)
if err != nil {
return 0, err
}
if n != 1 {
return 0, fmt.Errorf("Expected to read 1 byte, got %d", n)
}
return keys[0], nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment