109 lines
1.6 KiB
Go
109 lines
1.6 KiB
Go
package termi
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type KeyKind int
|
|
|
|
const (
|
|
KeyRune KeyKind = iota
|
|
KeyUp
|
|
KeyDown
|
|
KeyRight
|
|
KeyLeft
|
|
)
|
|
|
|
type Key struct {
|
|
Kind KeyKind
|
|
Rune rune
|
|
Raw string
|
|
}
|
|
|
|
const RuneEscape rune = 0x1b
|
|
const RuneEnter rune = '\r'
|
|
const RuneBackspace rune = '\b'
|
|
const RuneDelete rune = 0x7f
|
|
|
|
var buf []byte = make([]byte, 0)
|
|
|
|
func readByte() byte {
|
|
if len(buf) > 0 {
|
|
b := buf[0]
|
|
buf = buf[1:]
|
|
return b
|
|
}
|
|
|
|
b := make([]byte, 1)
|
|
_, err := io.ReadFull(os.Stdin, b)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b[0]
|
|
}
|
|
|
|
func runeSize(b byte) int {
|
|
switch {
|
|
case b&0x80 == 0:
|
|
return 1
|
|
case b&0xe0 == 0xc0:
|
|
return 2
|
|
case b&0xf0 == 0xe0:
|
|
return 3
|
|
case b&0xf8 == 0xf0:
|
|
return 4
|
|
default:
|
|
return -1 // invalid
|
|
}
|
|
}
|
|
|
|
func ReadKey() Key {
|
|
b := readByte()
|
|
if b != 0x1b { // Escape
|
|
expected := runeSize(b)
|
|
if expected == -1 {
|
|
panic("Invalid UTF-8 head")
|
|
}
|
|
full := make([]byte, expected)
|
|
full[0] = b
|
|
if expected > 1 {
|
|
_, err := io.ReadFull(os.Stdin, full[1:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
r, size := utf8.DecodeRune(full)
|
|
if r == utf8.RuneError && size == 1 {
|
|
panic("Invalid UTF-8 body")
|
|
}
|
|
return Key{KeyRune, r, ""}
|
|
}
|
|
|
|
seq := []byte{b}
|
|
|
|
b = readByte()
|
|
seq = append(seq, b)
|
|
if b != '[' {
|
|
buf = append(buf, seq[1:]...)
|
|
return Key{KeyRune, rune(seq[0]), ""}
|
|
}
|
|
|
|
b = readByte()
|
|
seq = append(seq, b)
|
|
switch b {
|
|
case 'A':
|
|
return Key{KeyUp, 0, string(seq)}
|
|
case 'B':
|
|
return Key{KeyDown, 0, string(seq)}
|
|
case 'C':
|
|
return Key{KeyRight, 0, string(seq)}
|
|
case 'D':
|
|
return Key{KeyLeft, 0, string(seq)}
|
|
}
|
|
|
|
buf = append(buf, seq[1:]...)
|
|
return Key{KeyRune, rune(seq[0]), ""}
|
|
}
|