161 lines
1.9 KiB
Go
161 lines
1.9 KiB
Go
package keyboard
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
)
|
|
|
|
type bytes [3]byte
|
|
|
|
type KeyEvent struct {
|
|
Bytes bytes
|
|
Key int
|
|
Char string
|
|
}
|
|
|
|
const (
|
|
Tilde = iota + 96
|
|
A
|
|
B
|
|
C
|
|
D
|
|
E
|
|
F
|
|
G
|
|
H
|
|
I
|
|
J
|
|
K
|
|
L
|
|
M
|
|
N
|
|
O
|
|
P
|
|
Q
|
|
R
|
|
S
|
|
T
|
|
U
|
|
V
|
|
W
|
|
X
|
|
Y
|
|
Z
|
|
)
|
|
|
|
const (
|
|
Escape = 27
|
|
Spacebar = 32
|
|
)
|
|
|
|
const (
|
|
Zero = iota + 48
|
|
One
|
|
Two
|
|
Three
|
|
Four
|
|
Five
|
|
Six
|
|
Seven
|
|
Eight
|
|
Nine
|
|
)
|
|
|
|
const (
|
|
ArrowUp = iota + 65
|
|
ArrowDown
|
|
ArrowRight
|
|
ArrowLeft
|
|
)
|
|
|
|
// used to hold the original stty state
|
|
var originalState string
|
|
|
|
func Parse(input bytes) KeyEvent {
|
|
var event = KeyEvent{Bytes: input, Char: string(input[:])}
|
|
|
|
var code byte
|
|
|
|
// basic input codes
|
|
if input[1] == 0 && input[2] == 0 {
|
|
code = input[0]
|
|
|
|
// space bar
|
|
if code == Spacebar {
|
|
event.Key = Spacebar
|
|
}
|
|
|
|
// vanilla escape
|
|
if code == Escape {
|
|
event.Key = Escape
|
|
}
|
|
|
|
// number keys
|
|
if code >= 48 && code <= 57 {
|
|
event.Key = int(code)
|
|
}
|
|
|
|
// alphabet
|
|
if code >= 97 && code <= 122 {
|
|
event.Key = int(code)
|
|
}
|
|
|
|
return event
|
|
}
|
|
|
|
// arrow keys
|
|
if input[0] == Escape && input[1] == 91 {
|
|
code = input[2]
|
|
|
|
if code >= 65 && code <= 68 {
|
|
event.Key = int(code)
|
|
return event
|
|
}
|
|
}
|
|
|
|
return event
|
|
}
|
|
|
|
// fetches original state, sets up TTY for raw (unbuffered) input
|
|
func configure() (err error) {
|
|
state, err := stty("-g")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
originalState = state
|
|
|
|
// -echo: terminal doesn't echo typed characters back to the terminal
|
|
// -icanon: terminal doesn't interpret special characters (like backspace)
|
|
if _, err := stty("-echo", "-icanon"); err != nil {
|
|
return err
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// restores the TTY to the original state
|
|
func restore() (err error) {
|
|
if _, err = stty("echo"); err != nil {
|
|
return
|
|
}
|
|
|
|
if _, err = stty(originalState); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func stty(args ...string) (string, error) {
|
|
cmd := exec.Command("stty", args...)
|
|
cmd.Stdin = os.Stdin
|
|
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(out), nil
|
|
}
|