add basic keyboard driver using stty
This commit is contained in:
parent
e7beae51bd
commit
15a764d865
|
@ -0,0 +1,36 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hybridgroup/gobot"
|
||||||
|
"github.com/hybridgroup/gobot/platforms/keyboard"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
gbot := gobot.NewGobot()
|
||||||
|
|
||||||
|
keys := keyboard.NewKeyboardDriver("keyboard")
|
||||||
|
|
||||||
|
work := func() {
|
||||||
|
gobot.On(keys.Event("key"), func(data interface{}) {
|
||||||
|
key := data.(keyboard.KeyEvent)
|
||||||
|
|
||||||
|
if key.Key == keyboard.A {
|
||||||
|
fmt.Println("A pressed!")
|
||||||
|
} else {
|
||||||
|
fmt.Println("keyboard event!", key, key.Char)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
robot := gobot.NewRobot("keyboardbot",
|
||||||
|
[]gobot.Connection{},
|
||||||
|
[]gobot.Device{keys},
|
||||||
|
work,
|
||||||
|
)
|
||||||
|
|
||||||
|
gbot.AddRobot(robot)
|
||||||
|
|
||||||
|
gbot.Start()
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright (c) 2014 The Hybrid Group
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Keyboard
|
||||||
|
|
||||||
|
This module implements support for keyboard events, wrapping the `stty` utility.
|
||||||
|
|
||||||
|
## How to Install
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/hybridgroup/gobot && go install github.com/hybridgroup/gobot/platforms/ardrone
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to Use
|
||||||
|
|
||||||
|
Example parsing key events
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hybridgroup/gobot"
|
||||||
|
"github.com/hybridgroup/gobot/platforms/keyboard"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
gbot := gobot.NewGobot()
|
||||||
|
|
||||||
|
keys := keyboard.NewKeyboardDriver("keyboard")
|
||||||
|
|
||||||
|
work := func() {
|
||||||
|
gobot.On(keys.Event("key"), func(data interface{}) {
|
||||||
|
key := data.(keyboard.KeyEvent)
|
||||||
|
|
||||||
|
if key.Key == keyboard.A {
|
||||||
|
fmt.Println("A pressed!")
|
||||||
|
} else {
|
||||||
|
fmt.Println("keyboard event!", key, key.Char)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
robot := gobot.NewRobot("keyboardbot",
|
||||||
|
[]gobot.Connection{},
|
||||||
|
[]gobot.Device{keys},
|
||||||
|
work,
|
||||||
|
)
|
||||||
|
|
||||||
|
gbot.AddRobot(robot)
|
||||||
|
|
||||||
|
gbot.Start()
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Packge keyboard contains the Gobot drivers for keyboard support.
|
||||||
|
|
||||||
|
Installing:
|
||||||
|
|
||||||
|
Then you can install the package with:
|
||||||
|
|
||||||
|
go get github.com/hybridgroup/gobot && go install github.com/hybridgroup/gobot/platforms/keyboard
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hybridgroup/gobot"
|
||||||
|
"github.com/hybridgroup/gobot/platforms/keyboard"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
gbot := gobot.NewGobot()
|
||||||
|
|
||||||
|
keys := keyboard.NewKeyboardDriver("keyboard")
|
||||||
|
|
||||||
|
work := func() {
|
||||||
|
gobot.On(keys.Event("key"), func(data interface{}) {
|
||||||
|
key := data.(keyboard.KeyEvent)
|
||||||
|
|
||||||
|
if key.Key == keyboard.A {
|
||||||
|
fmt.Println("A pressed!")
|
||||||
|
} else {
|
||||||
|
fmt.Println("keyboard event!", key, key.Char)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
robot := gobot.NewRobot("keyboardbot",
|
||||||
|
[]gobot.Connection{},
|
||||||
|
[]gobot.Device{keys},
|
||||||
|
work,
|
||||||
|
)
|
||||||
|
|
||||||
|
gbot.AddRobot(robot)
|
||||||
|
|
||||||
|
gbot.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
For further information refer to keyboard README:
|
||||||
|
https://github.com/hybridgroup/gobot/blob/master/platforms/keyboard/README.md
|
||||||
|
*/
|
||||||
|
package keyboard
|
|
@ -0,0 +1,156 @@
|
||||||
|
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() (errs []error) {
|
||||||
|
if _, err := stty(originalState); err != nil {
|
||||||
|
return []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package keyboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/hybridgroup/gobot"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ gobot.Driver = (*KeyboardDriver)(nil)
|
||||||
|
|
||||||
|
type KeyboardDriver struct {
|
||||||
|
name string
|
||||||
|
connect func(*KeyboardDriver) (err error)
|
||||||
|
listen func(*KeyboardDriver)
|
||||||
|
stdin *os.File
|
||||||
|
gobot.Eventer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeyboardDriver(name string) *KeyboardDriver {
|
||||||
|
k := &KeyboardDriver{
|
||||||
|
name: name,
|
||||||
|
connect: func(k *KeyboardDriver) (err error) {
|
||||||
|
if err := configure(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
k.stdin = os.Stdin
|
||||||
|
return
|
||||||
|
},
|
||||||
|
listen: func(k *KeyboardDriver) {
|
||||||
|
ctrlc := bytes{3}
|
||||||
|
|
||||||
|
for {
|
||||||
|
var keybuf bytes
|
||||||
|
k.stdin.Read(keybuf[0:3])
|
||||||
|
|
||||||
|
if keybuf == ctrlc {
|
||||||
|
proc, err := os.FindProcess(os.Getpid())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proc.Signal(os.Interrupt)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
gobot.Publish(k.Event("key"), Parse(keybuf))
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Eventer: gobot.NewEventer(),
|
||||||
|
}
|
||||||
|
|
||||||
|
k.AddEvent("key")
|
||||||
|
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KeyboardDriver) Name() string { return k.name }
|
||||||
|
func (k *KeyboardDriver) Connection() gobot.Connection { return nil }
|
||||||
|
|
||||||
|
// Start initializes keyboard by grabbing key events as they come in and
|
||||||
|
// publishing a key event
|
||||||
|
func (k *KeyboardDriver) Start() (errs []error) {
|
||||||
|
if err := k.connect(k); err != nil {
|
||||||
|
return []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
go k.listen(k)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halt stops camera driver
|
||||||
|
func (k *KeyboardDriver) Halt() (errs []error) {
|
||||||
|
if originalState != "" {
|
||||||
|
return restore()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package keyboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hybridgroup/gobot"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initTestKeyboardDriver() *KeyboardDriver {
|
||||||
|
d := NewKeyboardDriver("keyboard")
|
||||||
|
d.connect = func(k *KeyboardDriver) (err error) {
|
||||||
|
k.stdin = &os.File{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
d.listen = func(k *KeyboardDriver) {}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyboardDriver(t *testing.T) {
|
||||||
|
d := initTestKeyboardDriver()
|
||||||
|
gobot.Assert(t, d.Name(), "keyboard")
|
||||||
|
gobot.Assert(t, d.Connection(), (gobot.Connection)(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyboardDriverStart(t *testing.T) {
|
||||||
|
d := initTestKeyboardDriver()
|
||||||
|
gobot.Assert(t, len(d.Start()), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyboardDriverHalt(t *testing.T) {
|
||||||
|
d := initTestKeyboardDriver()
|
||||||
|
gobot.Assert(t, len(d.Halt()), 0)
|
||||||
|
}
|
Loading…
Reference in New Issue