Skip to content

Commit

Permalink
add basic keyboard driver using stty
Browse files Browse the repository at this point in the history
  • Loading branch information
stewart committed Jul 9, 2015
1 parent e7beae5 commit 15a764d
Show file tree
Hide file tree
Showing 7 changed files with 424 additions and 0 deletions.
36 changes: 36 additions & 0 deletions examples/keyboard.go
Original file line number Diff line number Diff line change
@@ -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()
}
13 changes: 13 additions & 0 deletions platforms/keyboard/LICENSE
Original file line number Diff line number Diff line change
@@ -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.
52 changes: 52 additions & 0 deletions platforms/keyboard/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 52 additions & 0 deletions platforms/keyboard/doc.go
Original file line number Diff line number Diff line change
@@ -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
156 changes: 156 additions & 0 deletions platforms/keyboard/keyboard.go
Original file line number Diff line number Diff line change
@@ -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
}
81 changes: 81 additions & 0 deletions platforms/keyboard/keyboard_driver.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 15a764d

Please sign in to comment.