-
Notifications
You must be signed in to change notification settings - Fork 724
/
key.go
278 lines (255 loc) Β· 6.83 KB
/
key.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
package tea
import (
"errors"
"fmt"
"io"
"unicode/utf8"
)
// KeyMsg contains information about a keypress.
type KeyMsg Key
// String returns a friendly name for a key.
func (k *KeyMsg) String() (str string) {
if k.Alt {
str += "alt+"
}
if k.Type == KeyRune {
str += string(k.Rune)
return str
} else if s, ok := keyNames[int(k.Type)]; ok {
str += s
return str
}
return ""
}
// IsRune returns weather or not the key is a rune.
func (k *KeyMsg) IsRune() bool {
return k.Type == KeyRune
}
// Key contains information about a keypress.
type Key struct {
Type KeyType
Rune rune
Alt bool
}
// KeyType indicates the key pressed.
type KeyType int
// Control keys. I know we could do this with an iota, but the values are very
// specific, so we set the values explicitly to avoid any confusion.
//
// See also:
// https://en.wikipedia.org/wiki/C0_and_C1_control_codes
const (
keyNUL = 0 // null, \0
keySOH = 1 // start of heading
keySTX = 2 // start of text
keyETX = 3 // break, ctrl+c
keyEOT = 4 // end of transmission
keyENQ = 5 // enquiry
keyACK = 6 // acknowledge
keyBEL = 7 // bell, \a
keyBS = 8 // backspace
keyHT = 9 // horizontal tabulation, \t
keyLF = 10 // line feed, \n
keyVT = 11 // vertical tabulation \v
keyFF = 12 // form feed \f
keyCR = 13 // carriage return, \r
keySO = 14 // shift out
keySI = 15 // shift in
keyDLE = 16 // data link escape
keyDC1 = 17 // device control one
keyDC2 = 18 // device control two
keyDC3 = 19 // device control three
keyDC4 = 20 // device control four
keyNAK = 21 // negative acknowledge
keySYN = 22 // synchronous idle
keyETB = 23 // end of transmission block
keyCAN = 24 // cancel
keyEM = 25 // end of medium
keySUB = 26 // substitution
keyESC = 27 // escape, \e
keyFS = 28 // file separator
keyGS = 29 // group separator
keyRS = 30 // record separator
keyUS = 31 // unit separator
keySP = 32 // space
keyDEL = 127 // delete. on most systems this is mapped to backspace, I hear
)
// Aliases
const (
KeyNull = keyNUL
KeyBreak = keyETX
KeyEnter = keyCR
KeyBackspace = keyBS
KeyTab = keyHT
KeySpace = keySP
KeyEsc = keyESC
KeyEscape = keyESC
KeyDelete = keyDEL
KeyCtrlAt = keyNUL // ctrl+@
KeyCtrlA = keySOH
KeyCtrlB = keySTX
KeyCtrlC = keyETX
KeyCtrlD = keyEOT
KeyCtrlE = keyENQ
KeyCtrlF = keyACK
KeyCtrlG = keyBEL
KeyCtrlH = keyBS
KeyCtrlI = keyHT
KeyCtrlJ = keyLF
KeyCtrlK = keyVT
KeyCtrlL = keyFF
KeyCtrlM = keyCR
KeyCtrlN = keySO
KeyCtrlO = keySI
KeyCtrlP = keyDLE
KeyCtrlQ = keyDC1
KeyCtrlR = keyDC2
KeyCtrlS = keyDC3
KeyCtrlT = keyDC4
KeyCtrlU = keyNAK
KeyCtrlV = keyETB
KeyCtrlW = keyETB
KeyCtrlX = keyCAN
KeyCtrlY = keyEM
KeyCtrlZ = keySUB
KeyCtrlOpenBracket = keyESC // ctrl+[
KeyCtrlBackslash = keyFS // ctrl+\
KeyCtrlCloseBracket = keyGS // ctrl+]
KeyCtrlCaret = keyRS // ctrl+^
KeyCtrlUnderscore = keyUS // ctrl+_
KeyCtrlQuestionMark = keyDEL // ctrl+?
)
// Other keys we track
const (
KeyRune = -(iota + 1)
KeyUp
KeyDown
KeyRight
KeyLeft
KeyShiftTab
KeyHome
KeyEnd
KeyPgUp
KeyPgDown
)
// Mapping for control keys to friendly consts
var keyNames = map[int]string{
keyNUL: "ctrl+@", // also ctrl+`
keySOH: "ctrl+a",
keySTX: "ctrl+b",
keyETX: "ctrl+c",
keyEOT: "ctrl+d",
keyENQ: "ctrl+e",
keyACK: "ctrl+f",
keyBEL: "ctrl+g",
keyBS: "backspace", // also ctrl+h
keyHT: "tab", // also ctrl+i
keyLF: "ctrl+j",
keyVT: "ctrl+k",
keyFF: "ctrl+l",
keyCR: "enter",
keySO: "ctrl+n",
keySI: "ctrl+o",
keyDLE: "ctrl+p",
keyDC1: "ctrl+q",
keyDC2: "ctrl+r",
keyDC3: "ctrl+s",
keyDC4: "ctrl+t",
keyNAK: "ctrl+u",
keySYN: "ctrl+v",
keyETB: "ctrl+w",
keyCAN: "ctrl+x",
keyEM: "ctrl+y",
keySUB: "ctrl+z",
keyESC: "esc",
keyFS: "ctrl+\\",
keyGS: "ctrl+]",
keyRS: "ctrl+^",
keyUS: "ctrl+_",
keySP: "space",
keyDEL: "delete",
KeyRune: "rune",
KeyUp: "up",
KeyDown: "down",
KeyRight: "right",
KeyLeft: "left",
KeyShiftTab: "shift+tab",
KeyHome: "home",
KeyEnd: "end",
KeyPgUp: "pgup",
KeyPgDown: "pgdown",
}
// Mapping for sequences to consts
// TODO: should we just move this into the hex table?
var sequences = map[string]KeyType{
"\x1b[A": KeyUp,
"\x1b[B": KeyDown,
"\x1b[C": KeyRight,
"\x1b[D": KeyLeft,
}
// Mapping for hex codes to consts. Unclear why these won't register as
// sequences.
var hexes = map[string]Key{
"1b5b5a": {Type: KeyShiftTab},
"1b0d": {Type: KeyEnter, Alt: true},
"1b7f": {Type: KeyDelete, Alt: true},
"1b5b48": {Type: KeyHome},
"1b5b377e": {Type: KeyHome}, // urxvt
"1b5b313b3348": {Type: KeyHome, Alt: true},
"1b1b5b377e": {Type: KeyHome, Alt: true}, // ursvt
"1b5b46": {Type: KeyEnd},
"1b5b387e": {Type: KeyEnd}, // urxvt
"1b5b313b3346": {Type: KeyEnd, Alt: true},
"1b1b5b387e": {Type: KeyEnd, Alt: true}, // urxvt
"1b5b357e": {Type: KeyPgUp},
"1b5b353b337e": {Type: KeyPgUp, Alt: true},
"1b1b5b357e": {Type: KeyPgUp, Alt: true}, // urxvt
"1b5b367e": {Type: KeyPgDown},
"1b5b363b337e": {Type: KeyPgDown, Alt: true},
"1b1b5b367e": {Type: KeyPgDown, Alt: true}, // urxvt
"1b5b313b3341": {Type: KeyUp, Alt: true},
"1b5b313b3342": {Type: KeyDown, Alt: true},
"1b5b313b3343": {Type: KeyRight, Alt: true},
"1b5b313b3344": {Type: KeyLeft, Alt: true},
}
// ReadKey reads keypress input from a TTY and returns a string representation
// of a key.
func ReadKey(r io.Reader) (Key, error) {
var buf [256]byte
// Read and block
numBytes, err := r.Read(buf[:])
if err != nil {
return Key{}, err
}
hex := fmt.Sprintf("%x", buf[:numBytes])
// Some of these need special handling
if k, ok := hexes[hex]; ok {
return k, nil
}
// Get unicode value
char, _ := utf8.DecodeRune(buf[:])
if char == utf8.RuneError {
return Key{}, errors.New("could not decode rune")
}
// Is it a control character?
if numBytes == 1 && char <= keyUS || char == keyDEL {
return Key{Type: KeyType(char)}, nil
}
// Is it a special sequence, like an arrow key?
if k, ok := sequences[string(buf[:numBytes])]; ok {
return Key{Type: k}, nil
}
// Is the alt key pressed? The buffer will be prefixed with an escape
// sequence if so
if numBytes > 1 && buf[0] == 0x1b {
// Now remove the initial escape sequence and re-process to get the
// character.
c, _ := utf8.DecodeRune(buf[1:])
if c == utf8.RuneError {
return Key{}, errors.New("could not decode rune after removing initial escape")
}
return Key{Alt: true, Type: KeyRune, Rune: c}, nil
}
// Just a regular, ol' rune
return Key{Type: KeyRune, Rune: char}, nil
}