diff --git a/examples/keyboard.go b/examples/keyboard.go new file mode 100644 index 00000000..e07eee27 --- /dev/null +++ b/examples/keyboard.go @@ -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() +} diff --git a/platforms/keyboard/LICENSE b/platforms/keyboard/LICENSE new file mode 100644 index 00000000..122c97aa --- /dev/null +++ b/platforms/keyboard/LICENSE @@ -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. diff --git a/platforms/keyboard/README.md b/platforms/keyboard/README.md new file mode 100644 index 00000000..079a1c24 --- /dev/null +++ b/platforms/keyboard/README.md @@ -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() +} +``` diff --git a/platforms/keyboard/doc.go b/platforms/keyboard/doc.go new file mode 100644 index 00000000..cebdb850 --- /dev/null +++ b/platforms/keyboard/doc.go @@ -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 diff --git a/platforms/keyboard/keyboard.go b/platforms/keyboard/keyboard.go new file mode 100644 index 00000000..ba676e45 --- /dev/null +++ b/platforms/keyboard/keyboard.go @@ -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 +} diff --git a/platforms/keyboard/keyboard_driver.go b/platforms/keyboard/keyboard_driver.go new file mode 100644 index 00000000..bb355290 --- /dev/null +++ b/platforms/keyboard/keyboard_driver.go @@ -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 +} diff --git a/platforms/keyboard/keyboard_driver_test.go b/platforms/keyboard/keyboard_driver_test.go new file mode 100644 index 00000000..9d9ab688 --- /dev/null +++ b/platforms/keyboard/keyboard_driver_test.go @@ -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) +}