reline 0.2.5 → 0.2.8.pre.2
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 +46 -0
- data/lib/reline/ansi.rb +120 -56
- data/lib/reline/config.rb +39 -13
- data/lib/reline/general_io.rb +11 -3
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/line_editor.rb +347 -18
- data/lib/reline/terminfo.rb +126 -0
- data/lib/reline/unicode.rb +30 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +176 -80
- data/lib/reline.rb +83 -13
- metadata +6 -61
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'fiddle'
|
2
|
+
require 'fiddle/import'
|
3
|
+
|
4
|
+
module Reline::Terminfo
|
5
|
+
extend Fiddle::Importer
|
6
|
+
|
7
|
+
class TerminfoError < StandardError; end
|
8
|
+
|
9
|
+
def self.curses_dl_files
|
10
|
+
case RUBY_PLATFORM
|
11
|
+
when /mingw/, /mswin/
|
12
|
+
# aren't supported
|
13
|
+
[]
|
14
|
+
when /cygwin/
|
15
|
+
%w[cygncursesw-10.dll cygncurses-10.dll]
|
16
|
+
when /darwin/
|
17
|
+
%w[libncursesw.dylib libcursesw.dylib libncurses.dylib libcurses.dylib]
|
18
|
+
else
|
19
|
+
%w[libncursesw.so libcursesw.so libncurses.so libcurses.so]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
@curses_dl = false
|
24
|
+
def self.curses_dl
|
25
|
+
return @curses_dl unless @curses_dl == false
|
26
|
+
if RUBY_VERSION >= '3.0.0'
|
27
|
+
# Gem module isn't defined in test-all of the Ruby repository, and
|
28
|
+
# Fiddle in Ruby 3.0.0 or later supports Fiddle::TYPE_VARIADIC.
|
29
|
+
fiddle_supports_variadic = true
|
30
|
+
elsif Fiddle.const_defined?(:VERSION) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
|
31
|
+
# Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1.
|
32
|
+
fiddle_supports_variadic = true
|
33
|
+
else
|
34
|
+
fiddle_supports_variadic = false
|
35
|
+
end
|
36
|
+
if fiddle_supports_variadic and not Fiddle.const_defined?(:TYPE_VARIADIC)
|
37
|
+
# If the libffi version is not 3.0.5 or higher, there isn't TYPE_VARIADIC.
|
38
|
+
fiddle_supports_variadic = false
|
39
|
+
end
|
40
|
+
if fiddle_supports_variadic
|
41
|
+
curses_dl_files.each do |curses_name|
|
42
|
+
result = Fiddle::Handle.new(curses_name)
|
43
|
+
rescue Fiddle::DLError
|
44
|
+
next
|
45
|
+
else
|
46
|
+
@curses_dl = result
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@curses_dl = nil if @curses_dl == false
|
51
|
+
@curses_dl
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module Reline::Terminfo
|
56
|
+
dlload curses_dl
|
57
|
+
#extern 'int setupterm(char *term, int fildes, int *errret)'
|
58
|
+
@setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
59
|
+
#extern 'char *tigetstr(char *capname)'
|
60
|
+
@tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP)
|
61
|
+
begin
|
62
|
+
#extern 'char *tiparm(const char *str, ...)'
|
63
|
+
@tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
|
64
|
+
rescue Fiddle::DLError
|
65
|
+
# OpenBSD lacks tiparm
|
66
|
+
#extern 'char *tparm(const char *str, ...)'
|
67
|
+
@tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
|
68
|
+
end
|
69
|
+
# TODO: add int tigetflag(char *capname) and int tigetnum(char *capname)
|
70
|
+
|
71
|
+
def self.setupterm(term, fildes)
|
72
|
+
errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
|
73
|
+
ret = @setupterm.(term, fildes, errret_int)
|
74
|
+
errret = errret_int.unpack('i')[0]
|
75
|
+
case ret
|
76
|
+
when 0 # OK
|
77
|
+
0
|
78
|
+
when -1 # ERR
|
79
|
+
case errret
|
80
|
+
when 1
|
81
|
+
raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
|
82
|
+
when 0
|
83
|
+
raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.')
|
84
|
+
when -1
|
85
|
+
raise TerminfoError.new('The terminfo database could not be found.')
|
86
|
+
else # unknown
|
87
|
+
-1
|
88
|
+
end
|
89
|
+
else # unknown
|
90
|
+
-2
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class StringWithTiparm < String
|
95
|
+
def tiparm(*args) # for method chain
|
96
|
+
Reline::Terminfo.tiparm(self, *args)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.tigetstr(capname)
|
101
|
+
capability = @tigetstr.(capname)
|
102
|
+
case capability.to_i
|
103
|
+
when 0, -1
|
104
|
+
raise TerminfoError, "can't find capability: #{capname}"
|
105
|
+
end
|
106
|
+
StringWithTiparm.new(capability.to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.tiparm(str, *args)
|
110
|
+
new_args = []
|
111
|
+
args.each do |a|
|
112
|
+
new_args << Fiddle::TYPE_INT << a
|
113
|
+
end
|
114
|
+
@tiparm.(str, *new_args).to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.enabled?
|
118
|
+
true
|
119
|
+
end
|
120
|
+
end if Reline::Terminfo.curses_dl
|
121
|
+
|
122
|
+
module Reline::Terminfo
|
123
|
+
def self.enabled?
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end unless Reline::Terminfo.curses_dl
|
data/lib/reline/unicode.rb
CHANGED
@@ -185,6 +185,36 @@ class Reline::Unicode
|
|
185
185
|
[lines, height]
|
186
186
|
end
|
187
187
|
|
188
|
+
# Take a chunk of a String with escape sequences.
|
189
|
+
def self.take_range(str, col, length, encoding = str.encoding)
|
190
|
+
chunk = String.new(encoding: encoding)
|
191
|
+
width = 0
|
192
|
+
rest = str.encode(Encoding::UTF_8)
|
193
|
+
in_zero_width = false
|
194
|
+
rest.scan(WIDTH_SCANNER) do |gc|
|
195
|
+
case
|
196
|
+
when gc[NON_PRINTING_START_INDEX]
|
197
|
+
in_zero_width = true
|
198
|
+
when gc[NON_PRINTING_END_INDEX]
|
199
|
+
in_zero_width = false
|
200
|
+
when gc[CSI_REGEXP_INDEX]
|
201
|
+
chunk << gc[CSI_REGEXP_INDEX]
|
202
|
+
when gc[OSC_REGEXP_INDEX]
|
203
|
+
chunk << gc[OSC_REGEXP_INDEX]
|
204
|
+
when gc[GRAPHEME_CLUSTER_INDEX]
|
205
|
+
gc = gc[GRAPHEME_CLUSTER_INDEX]
|
206
|
+
if in_zero_width
|
207
|
+
chunk << gc
|
208
|
+
else
|
209
|
+
width = get_mbchar_width(gc)
|
210
|
+
break if (width + length) <= col
|
211
|
+
chunk << gc if col <= width
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
chunk
|
216
|
+
end
|
217
|
+
|
188
218
|
def self.get_next_mbchar_size(line, byte_pointer)
|
189
219
|
grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
|
190
220
|
grapheme ? grapheme.bytesize : 0
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -13,23 +13,44 @@ class Reline::Windows
|
|
13
13
|
@@legacy_console
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
16
|
+
def self.set_default_key_bindings(config)
|
17
|
+
{
|
18
|
+
[224, 72] => :ed_prev_history, # ↑
|
19
|
+
[224, 80] => :ed_next_history, # ↓
|
20
|
+
[224, 77] => :ed_next_char, # →
|
21
|
+
[224, 75] => :ed_prev_char, # ←
|
22
|
+
[224, 83] => :key_delete, # Del
|
23
|
+
[224, 71] => :ed_move_to_beg, # Home
|
24
|
+
[224, 79] => :ed_move_to_end, # End
|
25
|
+
[ 0, 41] => :ed_unassigned, # input method on/off
|
26
|
+
[ 0, 72] => :ed_prev_history, # ↑
|
27
|
+
[ 0, 80] => :ed_next_history, # ↓
|
28
|
+
[ 0, 77] => :ed_next_char, # →
|
29
|
+
[ 0, 75] => :ed_prev_char, # ←
|
30
|
+
[ 0, 83] => :key_delete, # Del
|
31
|
+
[ 0, 71] => :ed_move_to_beg, # Home
|
32
|
+
[ 0, 79] => :ed_move_to_end # End
|
33
|
+
}.each_pair do |key, func|
|
34
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
35
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
36
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
37
|
+
end
|
38
|
+
|
39
|
+
{
|
40
|
+
[27, 32] => :em_set_mark, # M-<space>
|
41
|
+
[24, 24] => :em_exchange_mark, # C-x C-x
|
42
|
+
}.each_pair do |key, func|
|
43
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Emulate ANSI key sequence.
|
47
|
+
{
|
48
|
+
[27, 91, 90] => :completion_journey_up, # S-Tab
|
49
|
+
}.each_pair do |key, func|
|
50
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
51
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
52
|
+
end
|
53
|
+
end
|
33
54
|
|
34
55
|
if defined? JRUBY_VERSION
|
35
56
|
require 'win32api'
|
@@ -73,13 +94,37 @@ class Reline::Windows
|
|
73
94
|
end
|
74
95
|
end
|
75
96
|
|
97
|
+
VK_RETURN = 0x0D
|
76
98
|
VK_MENU = 0x12
|
77
99
|
VK_LMENU = 0xA4
|
78
100
|
VK_CONTROL = 0x11
|
79
101
|
VK_SHIFT = 0x10
|
102
|
+
VK_DIVIDE = 0x6F
|
103
|
+
|
104
|
+
KEY_EVENT = 0x01
|
105
|
+
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
106
|
+
|
107
|
+
CAPSLOCK_ON = 0x0080
|
108
|
+
ENHANCED_KEY = 0x0100
|
109
|
+
LEFT_ALT_PRESSED = 0x0002
|
110
|
+
LEFT_CTRL_PRESSED = 0x0008
|
111
|
+
NUMLOCK_ON = 0x0020
|
112
|
+
RIGHT_ALT_PRESSED = 0x0001
|
113
|
+
RIGHT_CTRL_PRESSED = 0x0004
|
114
|
+
SCROLLLOCK_ON = 0x0040
|
115
|
+
SHIFT_PRESSED = 0x0010
|
116
|
+
|
117
|
+
VK_TAB = 0x09
|
118
|
+
VK_END = 0x23
|
119
|
+
VK_HOME = 0x24
|
120
|
+
VK_LEFT = 0x25
|
121
|
+
VK_UP = 0x26
|
122
|
+
VK_RIGHT = 0x27
|
123
|
+
VK_DOWN = 0x28
|
124
|
+
VK_DELETE = 0x2E
|
125
|
+
|
80
126
|
STD_INPUT_HANDLE = -10
|
81
127
|
STD_OUTPUT_HANDLE = -11
|
82
|
-
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
83
128
|
FILE_TYPE_PIPE = 0x0003
|
84
129
|
FILE_NAME_INFO = 2
|
85
130
|
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
@@ -93,13 +138,15 @@ class Reline::Windows
|
|
93
138
|
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
94
139
|
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
95
140
|
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
96
|
-
@@
|
141
|
+
@@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
|
97
142
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
98
143
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
99
144
|
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
145
|
+
@@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L')
|
100
146
|
|
101
147
|
@@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
102
148
|
@@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
149
|
+
@@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
|
103
150
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
104
151
|
|
105
152
|
private_class_method def self.getconsolemode
|
@@ -145,78 +192,74 @@ class Reline::Windows
|
|
145
192
|
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
146
193
|
end
|
147
194
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
195
|
+
KEY_MAP = [
|
196
|
+
# It's treated as Meta+Enter on Windows.
|
197
|
+
[ { control_keys: :CTRL, virtual_key_code: 0x0D }, "\e\r".bytes ],
|
198
|
+
[ { control_keys: :SHIFT, virtual_key_code: 0x0D }, "\e\r".bytes ],
|
199
|
+
|
200
|
+
# It's treated as Meta+Space on Windows.
|
201
|
+
[ { control_keys: :CTRL, char_code: 0x20 }, "\e ".bytes ],
|
202
|
+
|
203
|
+
# Emulate getwch() key sequences.
|
204
|
+
[ { control_keys: [], virtual_key_code: VK_UP }, [0, 72] ],
|
205
|
+
[ { control_keys: [], virtual_key_code: VK_DOWN }, [0, 80] ],
|
206
|
+
[ { control_keys: [], virtual_key_code: VK_RIGHT }, [0, 77] ],
|
207
|
+
[ { control_keys: [], virtual_key_code: VK_LEFT }, [0, 75] ],
|
208
|
+
[ { control_keys: [], virtual_key_code: VK_DELETE }, [0, 83] ],
|
209
|
+
[ { control_keys: [], virtual_key_code: VK_HOME }, [0, 71] ],
|
210
|
+
[ { control_keys: [], virtual_key_code: VK_END }, [0, 79] ],
|
211
|
+
|
212
|
+
# Emulate ANSI key sequence.
|
213
|
+
[ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ],
|
214
|
+
]
|
215
|
+
|
216
|
+
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
217
|
+
|
218
|
+
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
219
|
+
|
220
|
+
match = KEY_MAP.find { |args,| key.matches?(**args) }
|
221
|
+
unless match.nil?
|
222
|
+
@@output_buf.concat(match.last)
|
223
|
+
return
|
170
224
|
end
|
171
|
-
|
225
|
+
|
226
|
+
# no char, only control keys
|
227
|
+
return if key.char_code == 0 and key.control_keys.any?
|
228
|
+
|
229
|
+
@@output_buf.concat(key.char.bytes)
|
172
230
|
end
|
173
231
|
|
174
|
-
def self.
|
232
|
+
def self.check_input_event
|
175
233
|
num_of_events = 0.chr * 8
|
176
|
-
while @@
|
234
|
+
while @@output_buf.empty? #or true
|
235
|
+
next if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
236
|
+
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
|
177
237
|
input_record = 0.chr * 18
|
178
238
|
read_event = 0.chr * 4
|
179
|
-
if @@
|
239
|
+
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
180
240
|
event = input_record[0, 2].unpack('s*').first
|
181
|
-
|
241
|
+
case event
|
242
|
+
when WINDOW_BUFFER_SIZE_EVENT
|
182
243
|
@@winch_handler.()
|
244
|
+
when KEY_EVENT
|
245
|
+
key_down = input_record[4, 4].unpack('l*').first
|
246
|
+
repeat_count = input_record[8, 2].unpack('s*').first
|
247
|
+
virtual_key_code = input_record[10, 2].unpack('s*').first
|
248
|
+
virtual_scan_code = input_record[12, 2].unpack('s*').first
|
249
|
+
char_code = input_record[14, 2].unpack('S*').first
|
250
|
+
control_key_state = input_record[16, 2].unpack('S*').first
|
251
|
+
is_key_down = key_down.zero? ? false : true
|
252
|
+
if is_key_down
|
253
|
+
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
254
|
+
end
|
183
255
|
end
|
184
256
|
end
|
185
257
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
control = (@@GetKeyState.call(VK_CONTROL) & 0x80) != 0
|
192
|
-
shift = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0
|
193
|
-
force_enter = !input.instance_of?(Array) && (control or shift) && input == 0x0D
|
194
|
-
if force_enter
|
195
|
-
# It's treated as Meta+Enter on Windows
|
196
|
-
@@output_buf.push("\e".ord)
|
197
|
-
@@output_buf.push(input)
|
198
|
-
else
|
199
|
-
case input
|
200
|
-
when 0x00
|
201
|
-
meta = false
|
202
|
-
@@output_buf.push(input)
|
203
|
-
input = getwch
|
204
|
-
@@output_buf.push(*input)
|
205
|
-
when 0xE0
|
206
|
-
@@output_buf.push(input)
|
207
|
-
input = getwch
|
208
|
-
@@output_buf.push(*input)
|
209
|
-
when 0x03
|
210
|
-
@@output_buf.push(input)
|
211
|
-
else
|
212
|
-
@@output_buf.push(input)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
if meta
|
216
|
-
"\e".ord
|
217
|
-
else
|
218
|
-
@@output_buf.shift
|
219
|
-
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def self.getc
|
261
|
+
check_input_event
|
262
|
+
@@output_buf.shift
|
220
263
|
end
|
221
264
|
|
222
265
|
def self.ungetc(c)
|
@@ -313,6 +356,20 @@ class Reline::Windows
|
|
313
356
|
raise NotImplementedError
|
314
357
|
end
|
315
358
|
|
359
|
+
def self.hide_cursor
|
360
|
+
size = 100
|
361
|
+
visible = 0 # 0 means false
|
362
|
+
cursor_info = [size, visible].pack('Li')
|
363
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
364
|
+
end
|
365
|
+
|
366
|
+
def self.show_cursor
|
367
|
+
size = 100
|
368
|
+
visible = 1 # 1 means true
|
369
|
+
cursor_info = [size, visible].pack('Li')
|
370
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
371
|
+
end
|
372
|
+
|
316
373
|
def self.set_winch_handler(&handler)
|
317
374
|
@@winch_handler = handler
|
318
375
|
end
|
@@ -325,4 +382,43 @@ class Reline::Windows
|
|
325
382
|
def self.deprep(otio)
|
326
383
|
# do nothing
|
327
384
|
end
|
385
|
+
|
386
|
+
class KeyEventRecord
|
387
|
+
|
388
|
+
attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys
|
389
|
+
|
390
|
+
def initialize(virtual_key_code, char_code, control_key_state)
|
391
|
+
@virtual_key_code = virtual_key_code
|
392
|
+
@char_code = char_code
|
393
|
+
@control_key_state = control_key_state
|
394
|
+
@enhanced = control_key_state & ENHANCED_KEY != 0
|
395
|
+
|
396
|
+
(@control_keys = []).tap do |control_keys|
|
397
|
+
# symbols must be sorted to make comparison is easier later on
|
398
|
+
control_keys << :ALT if control_key_state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0
|
399
|
+
control_keys << :CTRL if control_key_state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0
|
400
|
+
control_keys << :SHIFT if control_key_state & SHIFT_PRESSED != 0
|
401
|
+
end.freeze
|
402
|
+
end
|
403
|
+
|
404
|
+
def char
|
405
|
+
@char_code.chr(Encoding::UTF_8)
|
406
|
+
end
|
407
|
+
|
408
|
+
def enhanced?
|
409
|
+
@enhanced
|
410
|
+
end
|
411
|
+
|
412
|
+
# Verifies if the arguments match with this key event.
|
413
|
+
# Nil arguments are ignored, but at least one must be passed as non-nil.
|
414
|
+
# To verify that no control keys were pressed, pass an empty array: `control_keys: []`.
|
415
|
+
def matches?(control_keys: nil, virtual_key_code: nil, char_code: nil)
|
416
|
+
raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil?
|
417
|
+
|
418
|
+
(control_keys.nil? || [*control_keys].sort == @control_keys) &&
|
419
|
+
(virtual_key_code.nil? || @virtual_key_code == virtual_key_code) &&
|
420
|
+
(char_code.nil? || char_code == @char_code)
|
421
|
+
end
|
422
|
+
|
423
|
+
end
|
328
424
|
end
|