terminal_rb 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/examples/info.rb +1 -0
- data/examples/key-codes.rb +6 -3
- data/examples/text.rb +32 -0
- data/lib/terminal/ansi.rb +133 -134
- data/lib/terminal/input/key_event.rb +165 -149
- data/lib/terminal/input.rb +113 -45
- data/lib/terminal/shell.rb +2 -6
- data/lib/terminal/text.rb +76 -80
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +29 -5
- data/terminal_rb.gemspec +34 -0
- metadata +10 -5
|
@@ -2,9 +2,85 @@
|
|
|
2
2
|
|
|
3
3
|
module Terminal
|
|
4
4
|
#
|
|
5
|
-
# Key event reported from {read_key_event}.
|
|
5
|
+
# Key event reported from {read_key_event} and {on_key_event}.
|
|
6
6
|
#
|
|
7
7
|
class KeyEvent
|
|
8
|
+
# ANSI code sequence received from standard input.
|
|
9
|
+
# This can be a simple value like `"a"`or more complex input like
|
|
10
|
+
# `"\e[24;6~"` (for Shift+Ctrl+F12).
|
|
11
|
+
#
|
|
12
|
+
# @return [String] received ANSI code sequence
|
|
13
|
+
attr_reader :raw
|
|
14
|
+
|
|
15
|
+
# Pressed key without any modifiers.
|
|
16
|
+
# This can be a string for simple keys like `"a"` or a Symbol like `:F12`
|
|
17
|
+
# (see {.key_names}).
|
|
18
|
+
# @return [String, Symbol] key without modifiers
|
|
19
|
+
attr_reader :key
|
|
20
|
+
|
|
21
|
+
# Modifier key code. This represents the encoded key modifier like `Shift`
|
|
22
|
+
# or `Alt`.
|
|
23
|
+
#
|
|
24
|
+
# @return [Integer] modifier key code
|
|
25
|
+
attr_reader :modifier
|
|
26
|
+
|
|
27
|
+
# Mouse event position.
|
|
28
|
+
#
|
|
29
|
+
# @return [[Integer,Integer]] row and column
|
|
30
|
+
# @return nil when no coordinates are assigned
|
|
31
|
+
attr_reader :position
|
|
32
|
+
|
|
33
|
+
# Name of the key event.
|
|
34
|
+
# This can be a simple name like `"a"` or `"Shift+Ctrl+F12"` for combined
|
|
35
|
+
# keys.
|
|
36
|
+
#
|
|
37
|
+
# @return [String] key name
|
|
38
|
+
attr_reader :name
|
|
39
|
+
|
|
40
|
+
# @attribute [r] modifier?
|
|
41
|
+
# @return [true, false] whether a key modifier was pressed
|
|
42
|
+
def modifier? = @modifier != 0
|
|
43
|
+
|
|
44
|
+
# @attribute [r] simple?
|
|
45
|
+
# @return [true, false] whether a simple key was pressed
|
|
46
|
+
def simple? = @raw == @name
|
|
47
|
+
|
|
48
|
+
# All pressed keys.
|
|
49
|
+
# This is composed by all {modifier} and the {key}.
|
|
50
|
+
#
|
|
51
|
+
# @return [Array<Symbol, String>] all pressed keys
|
|
52
|
+
def to_a = @ary.dup
|
|
53
|
+
|
|
54
|
+
# @private
|
|
55
|
+
def to_ary = simple? ? [@raw] : [@raw, @name]
|
|
56
|
+
|
|
57
|
+
# @private
|
|
58
|
+
def to_s = @name.dup
|
|
59
|
+
|
|
60
|
+
# @private
|
|
61
|
+
def inspect = "<#{self.class.name} #{to_ary.map(&:inspect).join(' ')}>"
|
|
62
|
+
|
|
63
|
+
# @private
|
|
64
|
+
def freeze
|
|
65
|
+
@raw.freeze
|
|
66
|
+
@key.freeze
|
|
67
|
+
@position.freeze
|
|
68
|
+
@name.freeze
|
|
69
|
+
super
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def initialize(raw, key, modifier, position)
|
|
75
|
+
@raw = raw
|
|
76
|
+
@key = key
|
|
77
|
+
@modifier = modifier
|
|
78
|
+
@position = position
|
|
79
|
+
@ary = MODIFIERS.filter_map { |b, n| n if modifier.allbits?(b) }
|
|
80
|
+
@ary << key if key
|
|
81
|
+
@name = @ary.join('+') # .encode(Encoding::UTF_8)
|
|
82
|
+
end
|
|
83
|
+
|
|
8
84
|
class << self
|
|
9
85
|
# @attribute [w] caching
|
|
10
86
|
# @return [true, false] whether {KeyEvent}s should be cached
|
|
@@ -12,24 +88,33 @@ module Terminal
|
|
|
12
88
|
|
|
13
89
|
# @attribute [w] caching
|
|
14
90
|
def caching=(value)
|
|
15
|
-
|
|
16
|
-
@cache ||= {}
|
|
17
|
-
else
|
|
18
|
-
@cache = nil
|
|
19
|
-
end
|
|
91
|
+
value ? @cache ||= {} : @cache = nil
|
|
20
92
|
end
|
|
21
93
|
|
|
22
|
-
#
|
|
94
|
+
# @attribute [r] key_names
|
|
95
|
+
# @return [Array<Symbol>] list of all avilable key names (see {key})
|
|
96
|
+
def key_names
|
|
97
|
+
(
|
|
98
|
+
@names ||=
|
|
99
|
+
(@csiu.values + @csi.values + @ss3.values)
|
|
100
|
+
.uniq
|
|
101
|
+
.keep_if { Symbol === _1 }
|
|
102
|
+
.sort!
|
|
103
|
+
).dup
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Translate a ANSI input code sequence into a related KeyEvent.
|
|
23
107
|
#
|
|
24
108
|
# @param raw [String] keyboard input
|
|
25
109
|
# @return [KeyEvent] related key event
|
|
26
110
|
def [](raw)
|
|
111
|
+
cached = @cache[raw] and return cached if @cache
|
|
27
112
|
return new(raw, *@single_key[raw.ord]) if raw.size == 1
|
|
28
113
|
return unknown(raw) if raw[0] != "\e"
|
|
29
114
|
return esc1(raw, raw[1]) if raw.size == 2 # ESC ?
|
|
30
115
|
case raw[1]
|
|
31
|
-
when "\e"
|
|
32
|
-
return esc_esc(raw)
|
|
116
|
+
when "\e"
|
|
117
|
+
return esc_esc(raw) # ESC ESC ...
|
|
33
118
|
when 'O'
|
|
34
119
|
return new(raw, *@ss3[raw[2].ord]) if raw.size == 3 # ESC O ?
|
|
35
120
|
when '['
|
|
@@ -37,15 +122,17 @@ module Terminal
|
|
|
37
122
|
if raw.size == 6 && raw[2] == 'M' # ESC [ M b c r
|
|
38
123
|
return mouse_vt200(raw)
|
|
39
124
|
end
|
|
40
|
-
|
|
125
|
+
if raw.size > 4 && raw[2] == '1' && raw[3] == ';'
|
|
126
|
+
return csi(raw) # ESC [ 1 ; ...
|
|
127
|
+
end
|
|
41
128
|
case raw[-1]
|
|
42
|
-
when '~'
|
|
43
|
-
return legacy(raw)
|
|
44
|
-
when 'u'
|
|
45
|
-
return csi_u(raw)
|
|
46
|
-
when 'M'
|
|
47
|
-
|
|
48
|
-
|
|
129
|
+
when '~'
|
|
130
|
+
return legacy(raw) # ESC [ ... ~
|
|
131
|
+
when 'u'
|
|
132
|
+
return csi_u(raw) # ESC [ ... u
|
|
133
|
+
when 'M', 'm'
|
|
134
|
+
# ESC [ ... M
|
|
135
|
+
# ESC [ ... m
|
|
49
136
|
return mouse_sgr(raw) if raw[2] == '<'
|
|
50
137
|
end
|
|
51
138
|
end
|
|
@@ -53,40 +140,29 @@ module Terminal
|
|
|
53
140
|
end
|
|
54
141
|
|
|
55
142
|
# @private
|
|
56
|
-
def new(raw, key = raw, modifier = 0,
|
|
57
|
-
@cache ?
|
|
143
|
+
def new(raw, key = raw, modifier = 0, position = nil)
|
|
144
|
+
(@cache && position.nil? ? @cache[raw] ||= super : super).freeze
|
|
58
145
|
end
|
|
59
146
|
|
|
60
147
|
private
|
|
61
148
|
|
|
62
149
|
def unknown(raw) = new(raw, nil)
|
|
63
150
|
|
|
64
|
-
def
|
|
65
|
-
# ESC
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
idx = raw.index(';')
|
|
69
|
-
code = raw[2..(idx ? idx - 1 : -2)].to_i
|
|
70
|
-
new(
|
|
71
|
-
raw,
|
|
72
|
-
@csiu[code] || code.chr(Encoding::UTF_8),
|
|
73
|
-
idx ? [raw[(idx + 1)..-2].to_i - 1, 0].max : 0
|
|
74
|
-
)
|
|
151
|
+
def esc1(raw, char)
|
|
152
|
+
# ESC ?
|
|
153
|
+
key, modifier = @esc1[char.ord]
|
|
154
|
+
new(raw, key || char, modifier || 2)
|
|
75
155
|
end
|
|
76
156
|
|
|
77
|
-
def
|
|
78
|
-
# ESC
|
|
79
|
-
# ESC
|
|
80
|
-
return
|
|
81
|
-
|
|
82
|
-
new(
|
|
83
|
-
raw,
|
|
84
|
-
@csi[raw[2..(idx ? idx - 1 : -2)].to_i],
|
|
85
|
-
idx ? [raw[(idx + 1)..-2].to_i - 1, 0].max : 0
|
|
86
|
-
)
|
|
157
|
+
def esc_esc(raw)
|
|
158
|
+
# ESC ESC ?
|
|
159
|
+
# ESC ESC ...
|
|
160
|
+
return esc1(raw, raw[2]) if raw.size == 3
|
|
161
|
+
ret = self[raw[1..]]
|
|
162
|
+
new(raw, ret.key, ret.modifier | 2)
|
|
87
163
|
end
|
|
88
164
|
|
|
89
|
-
def
|
|
165
|
+
def csi(raw)
|
|
90
166
|
# ESC [ 1 ; [~ABCDEFHPQRS]
|
|
91
167
|
# ESC [ 1 ; <modifier> [~ABCDEFHPQRS]
|
|
92
168
|
return unknown(raw) if raw.size < 5
|
|
@@ -97,19 +173,33 @@ module Terminal
|
|
|
97
173
|
new(raw, key, modifier)
|
|
98
174
|
end
|
|
99
175
|
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
176
|
+
def legacy(raw)
|
|
177
|
+
# ESC [ <code> ~
|
|
178
|
+
# ESC [ <code> ; <modifier> ~
|
|
179
|
+
return unknown(raw) if raw.size < 4
|
|
180
|
+
idx = raw.index(';')
|
|
181
|
+
new(
|
|
182
|
+
raw,
|
|
183
|
+
@csi[raw[2..(idx ? idx - 1 : -2)].to_i],
|
|
184
|
+
idx ? [raw[(idx + 1)..-2].to_i - 1, 0].max : 0
|
|
185
|
+
)
|
|
104
186
|
end
|
|
105
187
|
|
|
106
|
-
def
|
|
107
|
-
|
|
108
|
-
|
|
188
|
+
def csi_u(raw)
|
|
189
|
+
# ESC [ <code> u
|
|
190
|
+
# ESC [ <code> ; <modifier> u
|
|
191
|
+
return unknown(raw) if raw.size < 4
|
|
192
|
+
idx = raw.index(';')
|
|
193
|
+
code = raw[2..(idx ? idx - 1 : -2)].to_i
|
|
194
|
+
new(
|
|
195
|
+
raw,
|
|
196
|
+
@csiu[code] || code.chr(Encoding::UTF_8),
|
|
197
|
+
idx ? [raw[(idx + 1)..-2].to_i - 1, 0].max : 0
|
|
198
|
+
)
|
|
109
199
|
end
|
|
110
200
|
|
|
111
201
|
def mouse_vt200(raw)
|
|
112
|
-
# ESC [ M
|
|
202
|
+
# ESC [ M <btn> <col> <row>
|
|
113
203
|
mouse_event(raw, *raw[3..].chars.map! { _1.ord - 32 })
|
|
114
204
|
end
|
|
115
205
|
|
|
@@ -118,117 +208,40 @@ module Terminal
|
|
|
118
208
|
# ESC [ < <code> ; <col> ; <row> m
|
|
119
209
|
return unknown(raw) if raw.size < 8
|
|
120
210
|
bcr = raw[3..-2].split(';', 3).map!(&:to_i)
|
|
121
|
-
bcr.size == 3 ? mouse_event(raw, *bcr) : unknown(raw)
|
|
211
|
+
bcr.size == 3 ? mouse_event(raw, *bcr, raw[-1] == 'm') : unknown(raw)
|
|
122
212
|
end
|
|
123
213
|
|
|
124
|
-
def mouse_urxvt(raw)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
bcr.size == 3 ? mouse_event(raw, *bcr) : unknown(raw)
|
|
129
|
-
end
|
|
214
|
+
# def mouse_urxvt(raw)
|
|
215
|
+
# # ESC [ <code> ; <col> ; <row> M
|
|
216
|
+
# unknown(raw) # currently not supported
|
|
217
|
+
# end
|
|
130
218
|
|
|
131
|
-
def mouse_event(raw, btn, col, row)
|
|
219
|
+
def mouse_event(raw, btn, col, row, up = false)
|
|
132
220
|
return unknown(raw) if btn < 0 || col < 1 || row < 1
|
|
133
|
-
key
|
|
221
|
+
key = (btn & 1).nonzero? ? 1 : 0
|
|
222
|
+
key += 2 if btn.allbits?(2)
|
|
134
223
|
modifier = btn.allbits?(4) ? 1 : 0
|
|
135
224
|
modifier += 2 if btn.allbits?(8)
|
|
136
225
|
modifier += 4 if btn.allbits?(16)
|
|
137
|
-
|
|
226
|
+
modifier += 256 if moved = btn.allbits?(32)
|
|
227
|
+
key += 4 if btn.allbits?(64)
|
|
228
|
+
key += 8 if btn.allbits?(128)
|
|
229
|
+
new(raw, mouse_name(key, up, moved), modifier, [row, col])
|
|
138
230
|
end
|
|
139
231
|
|
|
140
|
-
def
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
232
|
+
def mouse_name(key, up, moved)
|
|
233
|
+
return up ? :MBLeftUp : :MBLeft if key == 0
|
|
234
|
+
return up ? :MBMiddleUp : :MBMiddle if key == 1
|
|
235
|
+
return up ? :MBRightUp : :MBRight if key == 2
|
|
236
|
+
return moved ? :Mouse : :MBUp if key == 3
|
|
237
|
+
return :MWUp if key == 4
|
|
238
|
+
return :MWDown if key == 5
|
|
239
|
+
return :MWLeft if key == 6
|
|
240
|
+
return :MWRight if key == 7
|
|
241
|
+
:"MB_#{key - 8}#{'Up' if up}"
|
|
145
242
|
end
|
|
146
243
|
end
|
|
147
244
|
|
|
148
|
-
# Event string received from standard input.
|
|
149
|
-
# This can be a simple value like `"a"`or more complex input like
|
|
150
|
-
# `"\e[24;6~"` (for Shift+Ctrl+F12).
|
|
151
|
-
#
|
|
152
|
-
# @return [String] received event string
|
|
153
|
-
attr_reader :raw
|
|
154
|
-
|
|
155
|
-
# Pressed key without any modifiers.
|
|
156
|
-
# This can be a string for simple keys like `"a"` or a Symbol like `:F12`.
|
|
157
|
-
# @return [String, Symbol] key without modifiers
|
|
158
|
-
attr_reader :key
|
|
159
|
-
|
|
160
|
-
# Modifier key code. This represents the encoded key modifier like `Shift`
|
|
161
|
-
# or `Alt`.
|
|
162
|
-
#
|
|
163
|
-
# @return [Integer] modifier key code
|
|
164
|
-
attr_reader :modifier
|
|
165
|
-
|
|
166
|
-
# @comment for mouse events
|
|
167
|
-
# @private
|
|
168
|
-
attr_reader :extra
|
|
169
|
-
|
|
170
|
-
# Name of the key event.
|
|
171
|
-
# This can be a simple name like `"a"` or `"Shift+Ctrl+F12"` for combined
|
|
172
|
-
# keys.
|
|
173
|
-
#
|
|
174
|
-
# @return [String] key name
|
|
175
|
-
attr_reader :name
|
|
176
|
-
|
|
177
|
-
# @attribute [r] modifier?
|
|
178
|
-
# @return [true, false] whether a key modifier was pressed
|
|
179
|
-
def modifier? = @modifier != 0
|
|
180
|
-
|
|
181
|
-
# @attribute [r] simple?
|
|
182
|
-
# @return [true, false] whether a simple key was pressed
|
|
183
|
-
def simple? = @raw == @name
|
|
184
|
-
|
|
185
|
-
# All pressed keys.
|
|
186
|
-
# This is composed by all {modifier} and the {key}.
|
|
187
|
-
#
|
|
188
|
-
# @return [Array<Symbol, String>] all pressed keys
|
|
189
|
-
def to_a = @ary.dup
|
|
190
|
-
|
|
191
|
-
# @private
|
|
192
|
-
def to_ary = simple? ? [@raw] : [@raw, @name]
|
|
193
|
-
|
|
194
|
-
# @private
|
|
195
|
-
def to_s = @name.dup
|
|
196
|
-
|
|
197
|
-
# @private
|
|
198
|
-
def inspect = "<#{self.class.name} #{to_ary.map(&:inspect).join(' ')}>"
|
|
199
|
-
|
|
200
|
-
# @private
|
|
201
|
-
def freeze
|
|
202
|
-
@raw.freeze
|
|
203
|
-
@key.freeze
|
|
204
|
-
@extra.freeze
|
|
205
|
-
@name.freeze
|
|
206
|
-
super
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
private
|
|
210
|
-
|
|
211
|
-
def initialize(raw, key, modifier, extra)
|
|
212
|
-
@raw = raw
|
|
213
|
-
@key = key
|
|
214
|
-
@modifier = modifier
|
|
215
|
-
@extra = extra
|
|
216
|
-
@ary = MODIFIERS.filter_map { |b, n| n if modifier.allbits?(b) }
|
|
217
|
-
@ary << key if key
|
|
218
|
-
@name = @ary.join('+') # .encode(Encoding::UTF_8)
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
@mouse_kind = %i[
|
|
222
|
-
MButton1
|
|
223
|
-
MButton2
|
|
224
|
-
MButton3
|
|
225
|
-
MButtonUp
|
|
226
|
-
MButton4
|
|
227
|
-
MButton5
|
|
228
|
-
].freeze
|
|
229
|
-
|
|
230
|
-
@mouse_wheel_kind = %i[MWheelDown MWheelUp MWheelRight MWheelLeft].freeze
|
|
231
|
-
|
|
232
245
|
@csiu = {
|
|
233
246
|
0x02 => :Ins,
|
|
234
247
|
0x09 => :Tab,
|
|
@@ -357,6 +370,8 @@ module Terminal
|
|
|
357
370
|
0x44 => :Left, # D
|
|
358
371
|
0x46 => :End, # F
|
|
359
372
|
0x48 => :Home, # H
|
|
373
|
+
0x49 => :Focus, # I
|
|
374
|
+
0x4f => :UnFocus, # O
|
|
360
375
|
0x50 => :F1, # P
|
|
361
376
|
0x51 => :F2, # Q
|
|
362
377
|
0x52 => :F3, # R
|
|
@@ -421,7 +436,8 @@ module Terminal
|
|
|
421
436
|
16 => :Hyper,
|
|
422
437
|
32 => :Meta,
|
|
423
438
|
64 => :Caps,
|
|
424
|
-
128 => :Num
|
|
439
|
+
128 => :Num,
|
|
440
|
+
256 => :Move # mouse
|
|
425
441
|
}.freeze
|
|
426
442
|
private_constant :MODIFIERS
|
|
427
443
|
end
|
data/lib/terminal/input.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Terminal
|
|
|
16
16
|
# @return [:dumb]
|
|
17
17
|
# for non-interactive input (pipes etc.)
|
|
18
18
|
# @return [:error]
|
|
19
|
-
# when input device is closed
|
|
19
|
+
# when input device is not avail (closed)
|
|
20
20
|
def input_mode
|
|
21
21
|
@input_mode ||= find_input_mode
|
|
22
22
|
end
|
|
@@ -34,6 +34,10 @@ module Terminal
|
|
|
34
34
|
# @return [[String, String]] key code and key name in `:both` mode
|
|
35
35
|
# @return [nil] in error case
|
|
36
36
|
def read_key(mode: :named)
|
|
37
|
+
warn(
|
|
38
|
+
'Terminal.read_key is deprecaded; use Terminal.read_key_event instead.',
|
|
39
|
+
uplevel: 1
|
|
40
|
+
)
|
|
37
41
|
event = read_key_event or return
|
|
38
42
|
return event.raw if mode == :raw
|
|
39
43
|
key, name = event
|
|
@@ -48,25 +52,109 @@ module Terminal
|
|
|
48
52
|
case input_mode
|
|
49
53
|
when :dumb
|
|
50
54
|
raw = read_dumb or return
|
|
51
|
-
opts =
|
|
55
|
+
opts = @dumb_keys[raw.ord] if raw.size == 1
|
|
52
56
|
KeyEvent.new(raw, *opts)
|
|
53
57
|
when :csi_u, :legacy
|
|
54
|
-
|
|
55
|
-
KeyEvent[raw]
|
|
58
|
+
raw = read_tty or return
|
|
59
|
+
KeyEvent[raw]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Event loop for key and mouse events.
|
|
64
|
+
#
|
|
65
|
+
# @param mouse [true, false]
|
|
66
|
+
# whether mouse buttons should be reported
|
|
67
|
+
# @param mouse_move [true, false]
|
|
68
|
+
# whether mouse movement should be reported
|
|
69
|
+
# @param focus [true, false]
|
|
70
|
+
# whether focus/unfocus of terminal window should be reported
|
|
71
|
+
# @yieldparam event [KeyEvent]
|
|
72
|
+
# next event
|
|
73
|
+
# @yieldreturn [true, false]
|
|
74
|
+
# whether the loop should be continued
|
|
75
|
+
# @return [true]
|
|
76
|
+
# when the loop was started
|
|
77
|
+
# @return [false]
|
|
78
|
+
# when the current input device is not available
|
|
79
|
+
# @return [nil]
|
|
80
|
+
# when no block was given
|
|
81
|
+
def on_key_event(mouse: false, mouse_move: false, focus: false, &block)
|
|
82
|
+
return unless block
|
|
83
|
+
case input_mode
|
|
84
|
+
when :dumb
|
|
85
|
+
on_bumb_key_event(&block)
|
|
86
|
+
true
|
|
87
|
+
when :csi_u, :legacy
|
|
88
|
+
on_tty_key_event(mouse, mouse_move, focus, &block)
|
|
89
|
+
true
|
|
90
|
+
else
|
|
91
|
+
false
|
|
56
92
|
end
|
|
57
93
|
end
|
|
58
94
|
|
|
59
95
|
private
|
|
60
96
|
|
|
97
|
+
def on_tty_key_event(mouse, mouse_move, focus)
|
|
98
|
+
# highlight: '1001'
|
|
99
|
+
# drag: '1002'
|
|
100
|
+
# move: '1003'
|
|
101
|
+
# focus: '1004' - lost: "\e[O", get: "\e[I"
|
|
102
|
+
# ext: '1005'
|
|
103
|
+
# sgr: '1006'
|
|
104
|
+
# urxvt: '1015'
|
|
105
|
+
# pixel: '1016'
|
|
106
|
+
opts =
|
|
107
|
+
if mouse || mouse_move || focus
|
|
108
|
+
opts = mouse ? +"\e[?1006;1000" : +"\e[?1006"
|
|
109
|
+
opts << ';1003' if mouse_move
|
|
110
|
+
opts << ';1004' if focus
|
|
111
|
+
raw_write("#{opts}h") ? "#{opts}l" : nil
|
|
112
|
+
end
|
|
113
|
+
STDIN.noecho do |stdin|
|
|
114
|
+
while (raw = stdin.getch)
|
|
115
|
+
if raw != "\e"
|
|
116
|
+
yield(KeyEvent[raw]) ? next : break
|
|
117
|
+
end
|
|
118
|
+
lesci = 0
|
|
119
|
+
while (nc = stdin.read_nonblock(1, exception: false))
|
|
120
|
+
break unless String === nc
|
|
121
|
+
lesci = raw.size if nc == "\e"
|
|
122
|
+
raw << nc
|
|
123
|
+
end
|
|
124
|
+
if lesci < 2
|
|
125
|
+
yield(KeyEvent[raw]) ? next : break
|
|
126
|
+
end
|
|
127
|
+
break unless raw[1..].split("\e").all? { yield(KeyEvent["\e#{_1}"]) }
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
rescue Interrupt
|
|
131
|
+
# nop
|
|
132
|
+
rescue IOError, SystemCallError
|
|
133
|
+
@input_mode = :error
|
|
134
|
+
ensure
|
|
135
|
+
raw_write(opts) if opts
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def on_bumb_key_event
|
|
139
|
+
while (raw = read_dumb)
|
|
140
|
+
opts = @dumb_keys[raw.ord] if raw.size == 1
|
|
141
|
+
return unless yield(KeyEvent.new(raw, *opts))
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
61
145
|
def find_input_mode
|
|
62
|
-
# order is important!
|
|
63
146
|
im = ENV['INPUT_MODE']
|
|
64
|
-
return :dumb if im == 'dumb'
|
|
65
147
|
return :legacy if im == 'legacy'
|
|
66
|
-
return :dumb
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
148
|
+
return :dumb if im == 'dumb' || !STDIN.tty?
|
|
149
|
+
STDIN.noecho do |stdin|
|
|
150
|
+
if raw_write("\e[>1u\e[?u\e[c")
|
|
151
|
+
inp = +''
|
|
152
|
+
inp << stdin.getch until inp.rindex('c')
|
|
153
|
+
if inp.include?("\e[?1u")
|
|
154
|
+
at_exit { raw_write("\e[<u") }
|
|
155
|
+
return :csi_u
|
|
156
|
+
end
|
|
157
|
+
end
|
|
70
158
|
end
|
|
71
159
|
:legacy
|
|
72
160
|
rescue Interrupt
|
|
@@ -75,12 +163,6 @@ module Terminal
|
|
|
75
163
|
:error
|
|
76
164
|
end
|
|
77
165
|
|
|
78
|
-
def csi_u?
|
|
79
|
-
inp = +''
|
|
80
|
-
STDIN.raw { inp << _1.getch until inp.rindex('c') }
|
|
81
|
-
inp.include?("\e[?1u")
|
|
82
|
-
end
|
|
83
|
-
|
|
84
166
|
def read_dumb
|
|
85
167
|
STDIN.getc
|
|
86
168
|
rescue Interrupt
|
|
@@ -90,45 +172,31 @@ module Terminal
|
|
|
90
172
|
nil
|
|
91
173
|
end
|
|
92
174
|
|
|
93
|
-
def read_tty_with_mouse(each_move: false)
|
|
94
|
-
# highlight: '1001'
|
|
95
|
-
# drag: '1002'
|
|
96
|
-
# move: '1003'
|
|
97
|
-
# ext: '1005'
|
|
98
|
-
# sgr: '1006'
|
|
99
|
-
# urxvt: '1015'
|
|
100
|
-
# pixel: '1016'
|
|
101
|
-
opts = each_move ? '1000;1003;1006;1015' : '1000;1006;1015'
|
|
102
|
-
opts = raw_write("\e[?#{opts}h") ? "\e[?#{opts}l" : nil
|
|
103
|
-
read_tty
|
|
104
|
-
ensure
|
|
105
|
-
raw_write(opts) if opts
|
|
106
|
-
end
|
|
107
|
-
|
|
108
175
|
def read_tty
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
176
|
+
STDIN.noecho do |stdin|
|
|
177
|
+
if (key = stdin.getch) == "\e"
|
|
178
|
+
while (nc = stdin.read_nonblock(1, exception: false))
|
|
179
|
+
String === nc ? key << nc : break
|
|
180
|
+
end
|
|
112
181
|
end
|
|
182
|
+
key
|
|
113
183
|
end
|
|
114
|
-
key
|
|
115
184
|
rescue Interrupt
|
|
116
185
|
nil
|
|
117
186
|
rescue IOError, SystemCallError
|
|
118
187
|
@input_mode = :error
|
|
119
188
|
nil
|
|
120
189
|
end
|
|
121
|
-
|
|
122
|
-
DUMB_KEYS = {
|
|
123
|
-
0x05 => ['c', 4],
|
|
124
|
-
0x08 => :Back,
|
|
125
|
-
0x09 => :Tab,
|
|
126
|
-
0x0a => :Enter,
|
|
127
|
-
0x0d => :Return,
|
|
128
|
-
0x1b => :Esc
|
|
129
|
-
}.compare_by_identity.freeze
|
|
130
|
-
private_constant :DUMB_KEYS
|
|
131
190
|
end
|
|
132
191
|
|
|
192
|
+
@dumb_keys = {
|
|
193
|
+
0x05 => ['c', 4],
|
|
194
|
+
0x08 => :Back,
|
|
195
|
+
0x09 => :Tab,
|
|
196
|
+
0x0a => :Enter,
|
|
197
|
+
0x0d => :Return,
|
|
198
|
+
0x1b => :Esc
|
|
199
|
+
}.compare_by_identity.freeze
|
|
200
|
+
|
|
133
201
|
autoload :KeyEvent, "#{__dir__}/input/key_event.rb"
|
|
134
202
|
end
|
data/lib/terminal/shell.rb
CHANGED
|
@@ -116,12 +116,8 @@ module Terminal
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
def initialize(enum)
|
|
119
|
-
@enum =
|
|
120
|
-
|
|
121
|
-
enum.enum_for(:each)
|
|
122
|
-
else
|
|
123
|
-
Enumerator.new { |y| enum.each { y << _1 } }
|
|
124
|
-
end
|
|
119
|
+
return @enum = enum.enum_for(:each) if enum.respond_to?(:enum_for)
|
|
120
|
+
@enum = Enumerator.new { |y| enum.each { y << _1 } }
|
|
125
121
|
end
|
|
126
122
|
end
|
|
127
123
|
end
|