Skip to content

Commit 15a764d

Browse files
committed
add basic keyboard driver using stty
1 parent e7beae5 commit 15a764d

File tree

7 files changed

+424
-0
lines changed

7 files changed

+424
-0
lines changed

examples/keyboard.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/hybridgroup/gobot"
7+
"github.com/hybridgroup/gobot/platforms/keyboard"
8+
)
9+
10+
func main() {
11+
gbot := gobot.NewGobot()
12+
13+
keys := keyboard.NewKeyboardDriver("keyboard")
14+
15+
work := func() {
16+
gobot.On(keys.Event("key"), func(data interface{}) {
17+
key := data.(keyboard.KeyEvent)
18+
19+
if key.Key == keyboard.A {
20+
fmt.Println("A pressed!")
21+
} else {
22+
fmt.Println("keyboard event!", key, key.Char)
23+
}
24+
})
25+
}
26+
27+
robot := gobot.NewRobot("keyboardbot",
28+
[]gobot.Connection{},
29+
[]gobot.Device{keys},
30+
work,
31+
)
32+
33+
gbot.AddRobot(robot)
34+
35+
gbot.Start()
36+
}

platforms/keyboard/LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright (c) 2014 The Hybrid Group
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

platforms/keyboard/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Keyboard
2+
3+
This module implements support for keyboard events, wrapping the `stty` utility.
4+
5+
## How to Install
6+
7+
```
8+
go get github.com/hybridgroup/gobot && go install github.com/hybridgroup/gobot/platforms/ardrone
9+
```
10+
11+
## How to Use
12+
13+
Example parsing key events
14+
15+
```go
16+
package main
17+
18+
import (
19+
"fmt"
20+
21+
"github.com/hybridgroup/gobot"
22+
"github.com/hybridgroup/gobot/platforms/keyboard"
23+
)
24+
25+
func main() {
26+
gbot := gobot.NewGobot()
27+
28+
keys := keyboard.NewKeyboardDriver("keyboard")
29+
30+
work := func() {
31+
gobot.On(keys.Event("key"), func(data interface{}) {
32+
key := data.(keyboard.KeyEvent)
33+
34+
if key.Key == keyboard.A {
35+
fmt.Println("A pressed!")
36+
} else {
37+
fmt.Println("keyboard event!", key, key.Char)
38+
}
39+
})
40+
}
41+
42+
robot := gobot.NewRobot("keyboardbot",
43+
[]gobot.Connection{},
44+
[]gobot.Device{keys},
45+
work,
46+
)
47+
48+
gbot.AddRobot(robot)
49+
50+
gbot.Start()
51+
}
52+
```

platforms/keyboard/doc.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Packge keyboard contains the Gobot drivers for keyboard support.
3+
4+
Installing:
5+
6+
Then you can install the package with:
7+
8+
go get github.com/hybridgroup/gobot && go install github.com/hybridgroup/gobot/platforms/keyboard
9+
10+
Example:
11+
12+
package main
13+
14+
import (
15+
"fmt"
16+
17+
"github.com/hybridgroup/gobot"
18+
"github.com/hybridgroup/gobot/platforms/keyboard"
19+
)
20+
21+
func main() {
22+
gbot := gobot.NewGobot()
23+
24+
keys := keyboard.NewKeyboardDriver("keyboard")
25+
26+
work := func() {
27+
gobot.On(keys.Event("key"), func(data interface{}) {
28+
key := data.(keyboard.KeyEvent)
29+
30+
if key.Key == keyboard.A {
31+
fmt.Println("A pressed!")
32+
} else {
33+
fmt.Println("keyboard event!", key, key.Char)
34+
}
35+
})
36+
}
37+
38+
robot := gobot.NewRobot("keyboardbot",
39+
[]gobot.Connection{},
40+
[]gobot.Device{keys},
41+
work,
42+
)
43+
44+
gbot.AddRobot(robot)
45+
46+
gbot.Start()
47+
}
48+
49+
For further information refer to keyboard README:
50+
https://github.com/hybridgroup/gobot/blob/master/platforms/keyboard/README.md
51+
*/
52+
package keyboard

platforms/keyboard/keyboard.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package keyboard
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
)
7+
8+
type bytes [3]byte
9+
10+
type KeyEvent struct {
11+
Bytes bytes
12+
Key int
13+
Char string
14+
}
15+
16+
const (
17+
Tilde = iota + 96
18+
A
19+
B
20+
C
21+
D
22+
E
23+
F
24+
G
25+
H
26+
I
27+
J
28+
K
29+
L
30+
M
31+
N
32+
O
33+
P
34+
Q
35+
R
36+
S
37+
T
38+
U
39+
V
40+
W
41+
X
42+
Y
43+
Z
44+
)
45+
46+
const (
47+
Escape = 27
48+
Spacebar = 32
49+
)
50+
51+
const (
52+
Zero = iota + 48
53+
One
54+
Two
55+
Three
56+
Four
57+
Five
58+
Six
59+
Seven
60+
Eight
61+
Nine
62+
)
63+
64+
const (
65+
ArrowUp = iota + 65
66+
ArrowDown
67+
ArrowRight
68+
ArrowLeft
69+
)
70+
71+
// used to hold the original stty state
72+
var originalState string
73+
74+
func Parse(input bytes) KeyEvent {
75+
var event = KeyEvent{Bytes: input, Char: string(input[:])}
76+
77+
var code byte
78+
79+
// basic input codes
80+
if input[1] == 0 && input[2] == 0 {
81+
code = input[0]
82+
83+
// space bar
84+
if code == Spacebar {
85+
event.Key = Spacebar
86+
}
87+
88+
// vanilla escape
89+
if code == Escape {
90+
event.Key = Escape
91+
}
92+
93+
// number keys
94+
if code >= 48 && code <= 57 {
95+
event.Key = int(code)
96+
}
97+
98+
// alphabet
99+
if code >= 97 && code <= 122 {
100+
event.Key = int(code)
101+
}
102+
103+
return event
104+
}
105+
106+
// arrow keys
107+
if input[0] == Escape && input[1] == 91 {
108+
code = input[2]
109+
110+
if code >= 65 && code <= 68 {
111+
event.Key = int(code)
112+
return event
113+
}
114+
}
115+
116+
return event
117+
}
118+
119+
// fetches original state, sets up TTY for raw (unbuffered) input
120+
func configure() (err error) {
121+
state, err := stty("-g")
122+
if err != nil {
123+
return err
124+
}
125+
126+
originalState = state
127+
128+
// -echo: terminal doesn't echo typed characters back to the terminal
129+
// -icanon: terminal doesn't interpret special characters (like backspace)
130+
if _, err := stty("-echo", "-icanon"); err != nil {
131+
return err
132+
}
133+
134+
return
135+
}
136+
137+
// restores the TTY to the original state
138+
func restore() (errs []error) {
139+
if _, err := stty(originalState); err != nil {
140+
return []error{err}
141+
}
142+
143+
return
144+
}
145+
146+
func stty(args ...string) (string, error) {
147+
cmd := exec.Command("stty", args...)
148+
cmd.Stdin = os.Stdin
149+
150+
out, err := cmd.Output()
151+
if err != nil {
152+
return "", err
153+
}
154+
155+
return string(out), nil
156+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package keyboard
2+
3+
import (
4+
"log"
5+
"os"
6+
7+
"github.com/hybridgroup/gobot"
8+
)
9+
10+
var _ gobot.Driver = (*KeyboardDriver)(nil)
11+
12+
type KeyboardDriver struct {
13+
name string
14+
connect func(*KeyboardDriver) (err error)
15+
listen func(*KeyboardDriver)
16+
stdin *os.File
17+
gobot.Eventer
18+
}
19+
20+
func NewKeyboardDriver(name string) *KeyboardDriver {
21+
k := &KeyboardDriver{
22+
name: name,
23+
connect: func(k *KeyboardDriver) (err error) {
24+
if err := configure(); err != nil {
25+
return err
26+
}
27+
28+
k.stdin = os.Stdin
29+
return
30+
},
31+
listen: func(k *KeyboardDriver) {
32+
ctrlc := bytes{3}
33+
34+
for {
35+
var keybuf bytes
36+
k.stdin.Read(keybuf[0:3])
37+
38+
if keybuf == ctrlc {
39+
proc, err := os.FindProcess(os.Getpid())
40+
if err != nil {
41+
log.Fatal(err)
42+
}
43+
44+
proc.Signal(os.Interrupt)
45+
break
46+
}
47+
48+
gobot.Publish(k.Event("key"), Parse(keybuf))
49+
50+
}
51+
},
52+
Eventer: gobot.NewEventer(),
53+
}
54+
55+
k.AddEvent("key")
56+
57+
return k
58+
}
59+
60+
func (k *KeyboardDriver) Name() string { return k.name }
61+
func (k *KeyboardDriver) Connection() gobot.Connection { return nil }
62+
63+
// Start initializes keyboard by grabbing key events as they come in and
64+
// publishing a key event
65+
func (k *KeyboardDriver) Start() (errs []error) {
66+
if err := k.connect(k); err != nil {
67+
return []error{err}
68+
}
69+
70+
go k.listen(k)
71+
72+
return
73+
}
74+
75+
// Halt stops camera driver
76+
func (k *KeyboardDriver) Halt() (errs []error) {
77+
if originalState != "" {
78+
return restore()
79+
}
80+
return
81+
}

0 commit comments

Comments
 (0)