From 65265786975a76d38fe5c90270dbcb57a95a4b7a Mon Sep 17 00:00:00 2001 From: sloum Date: Wed, 31 Mar 2021 23:29:58 -0700 Subject: [PATCH] Gets many keys working on the keyboard --- .gitignore | 1 + main.go | 136 +++++++++++++++++++++++++++++++++++++ termios/consts_linux.go | 10 +++ termios/consts_nonlinux.go | 10 +++ termios/termios.go | 77 +++++++++++++++++++++ 5 files changed, 234 insertions(+) create mode 100644 .gitignore create mode 100644 main.go create mode 100644 termios/consts_linux.go create mode 100644 termios/consts_nonlinux.go create mode 100644 termios/termios.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f5b8a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +lines diff --git a/main.go b/main.go new file mode 100644 index 0000000..0c30e31 --- /dev/null +++ b/main.go @@ -0,0 +1,136 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" + + "tildegit.org/sloum/lines/termios" +) + +const ( + UpArrow rune = iota - 20 + DownArrow + LeftArrow + RightArrow + Delete + Home + End + PrintScreen + PageUp + PageDown + Escape rune = 27 + NewLine rune = 10 + CarriageReturn rune = 13 + BackSpace rune = 127 +) + +func readKey() (rune, error) { + reader := bufio.NewReader(os.Stdin) + char, _, err := reader.ReadRune() + if err != nil { + return 0, err + } + avail := reader.Buffered() + if char == Escape && avail > 0 { + var b strings.Builder + b.WriteRune(27) + for ; avail > 0; avail-- { + c, _, e := reader.ReadRune() + if e != nil { + break + } + b.WriteRune(c) + } + escSeq := b.String() + switch true { + case escSeq == "\033[A": + char = UpArrow + case escSeq == "\033[B": + char = DownArrow + case escSeq == "\033[C": + char = RightArrow + case escSeq == "\033[D": + char = LeftArrow + case escSeq == "\033[5~": + char = PageUp + case escSeq == "\033[6~": + char = PageDown + case IsHomeKey(escSeq): + char = Home + case IsEndKey(escSeq): + char = End + case escSeq == "\033[3~" || escSeq == "\033[P": + char = Delete + default: + fmt.Printf("Odd sequence: %s\n", escSeq[1:]) + } + } + return char, nil +} + +func IsHomeKey(seq string) bool { + switch seq { + case "\033[1~", "\033[7~", "\033[H", "\033OH": + return true + default: + return false + } +} + +func IsEndKey(seq string) bool { + switch seq { + case "\033[4~", "\033[8~", "\033[F", "\033OF": + return true + default: + return false + } +} + +func PrintKey(k rune) { + switch true { + case k == UpArrow: + fmt.Println("UpArrow") + case k == DownArrow: + fmt.Println("DownArrow") + case k == LeftArrow: + fmt.Println("LeftArrow") + case k == RightArrow: + fmt.Println("RightArrow") + case k == Escape: + fmt.Println("Escape") + case k == NewLine: + fmt.Println("NewLine") + case k == CarriageReturn: + fmt.Println("CarriageReturn") + case k == Delete: + fmt.Println("Delete") + case k == Home: + fmt.Println("Home") + case k == End: + fmt.Println("End") + case k == BackSpace: + fmt.Println("BackSpace") + default: + fmt.Printf("%c (%d)\n", k, k) + } + +} + +func main() { + termios.SetCharMode() + + for { + k, e := readKey() + if e != nil { + fmt.Println(e.Error()) + continue + } + PrintKey(k) + if k == NewLine || k == CarriageReturn { + break + } + } + termios.Restore() +} diff --git a/termios/consts_linux.go b/termios/consts_linux.go new file mode 100644 index 0000000..ae6e076 --- /dev/null +++ b/termios/consts_linux.go @@ -0,0 +1,10 @@ +// +build linux + +package termios + +import "syscall" + +const ( + getTermiosIoctl = syscall.TCGETS + setTermiosIoctl = syscall.TCSETS +) diff --git a/termios/consts_nonlinux.go b/termios/consts_nonlinux.go new file mode 100644 index 0000000..ca0daf7 --- /dev/null +++ b/termios/consts_nonlinux.go @@ -0,0 +1,10 @@ +// +build !linux + +package termios + +import "syscall" + +const ( + getTermiosIoctl = syscall.TIOCGETA + setTermiosIoctl = syscall.TIOCSETAF +) diff --git a/termios/termios.go b/termios/termios.go new file mode 100644 index 0000000..cd67039 --- /dev/null +++ b/termios/termios.go @@ -0,0 +1,77 @@ +package termios + +import ( + "os" + "runtime" + "syscall" + "unsafe" +) + +type winsize struct { + Row uint16 + Col uint16 + Xpixel uint16 + Ypixel uint16 +} + +var fd = os.Stdin.Fd() +var initial = getTermios() + +func ioctl(fd, request, argp uintptr) error { + if _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, request, argp); e != 0 { + return e + } + return nil +} + +func GetWindowSize() (int, int) { + var value winsize + ioctl(fd, syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&value))) + return int(value.Col), int(value.Row) +} + +func getTermios() syscall.Termios { + var value syscall.Termios + err := ioctl(fd, getTermiosIoctl, uintptr(unsafe.Pointer(&value))) + if err != nil { + panic(err) + } + return value +} + +func setTermios(termios syscall.Termios) { + err := ioctl(fd, setTermiosIoctl, uintptr(unsafe.Pointer(&termios))) + if err != nil { + panic(err) + } + runtime.KeepAlive(termios) +} + +func SetCharMode() { + t := getTermios() + t.Lflag = t.Lflag ^ syscall.ICANON + t.Lflag = t.Lflag ^ syscall.ECHO + setTermios(t) +} + +func SetLineMode() { + var t = getTermios() + t.Lflag = t.Lflag | (syscall.ICANON | syscall.ECHO) + setTermios(t) +} + +func SetRawMode() { + t := getTermios() + t.Lflag = t.Lflag ^ syscall.ICANON + t.Lflag = t.Lflag ^ syscall.ECHO + t.Oflag = t.Oflag ^ syscall.OPOST + t.Iflag = t.Iflag ^ syscall.ICRNL + t.Iflag = t.Iflag ^ syscall.IXON + t.Cc[syscall.VMIN] = 0 + t.Cc[syscall.VTIME] = 1 + setTermios(t) +} + +func Restore() { + setTermios(initial) +}