1
1
Fork 0

Gets many keys working on the keyboard

This commit is contained in:
sloum 2021-03-31 23:29:58 -07:00
commit 6526578697
5 changed files with 234 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
lines

136
main.go Normal file
View File

@ -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()
}

10
termios/consts_linux.go Normal file
View File

@ -0,0 +1,10 @@
// +build linux
package termios
import "syscall"
const (
getTermiosIoctl = syscall.TCGETS
setTermiosIoctl = syscall.TCSETS
)

View File

@ -0,0 +1,10 @@
// +build !linux
package termios
import "syscall"
const (
getTermiosIoctl = syscall.TIOCGETA
setTermiosIoctl = syscall.TIOCSETAF
)

77
termios/termios.go Normal file
View File

@ -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)
}