add basic keyboard driver using stty

This commit is contained in:
Andrew Stewart 2015-07-09 11:33:46 -07:00
parent e7beae51bd
commit 15a764d865
7 changed files with 424 additions and 0 deletions

36
examples/keyboard.go Normal file
View File

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

View File

@ -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.

View File

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

52
platforms/keyboard/doc.go Normal file
View File

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

View File

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

View File

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

View File

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