mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[ruby/reline] Use ReadConsoleInputW() instead of getwch()
This needs https://github.com/aycabta/yamatanooroti/pull/19, which is released by yamatanooroti gem 0.0.7, to test with yamatanooroti. https://github.com/ruby/reline/commit/06c1f45da1
This commit is contained in:
parent
a049dfd10a
commit
c59bbd86a6
2 changed files with 97 additions and 62 deletions
|
@ -89,9 +89,30 @@ class Reline::Windows
|
||||||
VK_LMENU = 0xA4
|
VK_LMENU = 0xA4
|
||||||
VK_CONTROL = 0x11
|
VK_CONTROL = 0x11
|
||||||
VK_SHIFT = 0x10
|
VK_SHIFT = 0x10
|
||||||
|
|
||||||
|
KEY_EVENT = 0x01
|
||||||
|
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
||||||
|
|
||||||
|
CAPSLOCK_ON = 0x0080
|
||||||
|
ENHANCED_KEY = 0x0100
|
||||||
|
LEFT_ALT_PRESSED = 0x0002
|
||||||
|
LEFT_CTRL_PRESSED = 0x0008
|
||||||
|
NUMLOCK_ON = 0x0020
|
||||||
|
RIGHT_ALT_PRESSED = 0x0001
|
||||||
|
RIGHT_CTRL_PRESSED = 0x0004
|
||||||
|
SCROLLLOCK_ON = 0x0040
|
||||||
|
SHIFT_PRESSED = 0x0010
|
||||||
|
|
||||||
|
VK_END = 0x23
|
||||||
|
VK_HOME = 0x24
|
||||||
|
VK_LEFT = 0x25
|
||||||
|
VK_UP = 0x26
|
||||||
|
VK_RIGHT = 0x27
|
||||||
|
VK_DOWN = 0x28
|
||||||
|
VK_DELETE = 0x2E
|
||||||
|
|
||||||
STD_INPUT_HANDLE = -10
|
STD_INPUT_HANDLE = -10
|
||||||
STD_OUTPUT_HANDLE = -11
|
STD_OUTPUT_HANDLE = -11
|
||||||
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
|
||||||
FILE_TYPE_PIPE = 0x0003
|
FILE_TYPE_PIPE = 0x0003
|
||||||
FILE_NAME_INFO = 2
|
FILE_NAME_INFO = 2
|
||||||
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
||||||
|
@ -105,7 +126,7 @@ class Reline::Windows
|
||||||
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
||||||
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
||||||
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
||||||
@@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L')
|
@@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
|
||||||
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
||||||
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
||||||
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
||||||
|
@ -157,78 +178,69 @@ class Reline::Windows
|
||||||
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.getwch
|
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
||||||
unless @@input_buf.empty?
|
char = char_code.chr(Encoding::UTF_8)
|
||||||
return @@input_buf.shift
|
if char_code == 0x0D and control_key_state.anybits?(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | SHIFT_PRESSED)
|
||||||
end
|
# It's treated as Meta+Enter on Windows.
|
||||||
while @@kbhit.call == 0
|
@@output_buf.push("\e".ord)
|
||||||
sleep(0.001)
|
@@output_buf.push(char_code)
|
||||||
end
|
elsif control_key_state.anybits?(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
|
||||||
until @@kbhit.call == 0
|
@@output_buf.push("\e".ord)
|
||||||
ret = @@getwch.call
|
@@output_buf.concat(char.bytes)
|
||||||
if ret == 0 or ret == 0xE0
|
elsif control_key_state.anybits?(ENHANCED_KEY)
|
||||||
@@input_buf << ret
|
case virtual_key_code # Emulate getwch() key sequences.
|
||||||
ret = @@getwch.call
|
when VK_END
|
||||||
@@input_buf << ret
|
@@output_buf.push(0, 79)
|
||||||
return @@input_buf.shift
|
when VK_HOME
|
||||||
end
|
@@output_buf.push(0, 71)
|
||||||
begin
|
when VK_LEFT
|
||||||
bytes = ret.chr(Encoding::UTF_8).bytes
|
@@output_buf.push(0, 75)
|
||||||
@@input_buf.push(*bytes)
|
when VK_UP
|
||||||
rescue Encoding::UndefinedConversionError
|
@@output_buf.push(0, 72)
|
||||||
@@input_buf << ret
|
when VK_RIGHT
|
||||||
@@input_buf << @@getwch.call if ret == 224
|
@@output_buf.push(0, 77)
|
||||||
|
when VK_DOWN
|
||||||
|
@@output_buf.push(0, 80)
|
||||||
|
when VK_DELETE
|
||||||
|
@@output_buf.push(0, 83)
|
||||||
end
|
end
|
||||||
|
elsif char_code == 0 and control_key_state != 0
|
||||||
|
# unknown
|
||||||
|
else
|
||||||
|
@@output_buf.concat(char.bytes)
|
||||||
end
|
end
|
||||||
@@input_buf.shift
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.getc
|
def self.check_input_event
|
||||||
num_of_events = 0.chr * 8
|
num_of_events = 0.chr * 8
|
||||||
while @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) != 0 and num_of_events.unpack('L').first > 0
|
while @@output_buf.empty? #or true
|
||||||
|
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
|
||||||
input_record = 0.chr * 18
|
input_record = 0.chr * 18
|
||||||
read_event = 0.chr * 4
|
read_event = 0.chr * 4
|
||||||
if @@ReadConsoleInput.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
||||||
event = input_record[0, 2].unpack('s*').first
|
event = input_record[0, 2].unpack('s*').first
|
||||||
if event == WINDOW_BUFFER_SIZE_EVENT
|
case event
|
||||||
|
when WINDOW_BUFFER_SIZE_EVENT
|
||||||
@@winch_handler.()
|
@@winch_handler.()
|
||||||
|
when KEY_EVENT
|
||||||
|
key_down = input_record[4, 4].unpack('l*').first
|
||||||
|
repeat_count = input_record[8, 2].unpack('s*').first
|
||||||
|
virtual_key_code = input_record[10, 2].unpack('s*').first
|
||||||
|
virtual_scan_code = input_record[12, 2].unpack('s*').first
|
||||||
|
char_code = input_record[14, 2].unpack('S*').first
|
||||||
|
control_key_state = input_record[16, 2].unpack('S*').first
|
||||||
|
is_key_down = key_down.zero? ? false : true
|
||||||
|
if is_key_down
|
||||||
|
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
unless @@output_buf.empty?
|
end
|
||||||
return @@output_buf.shift
|
|
||||||
end
|
def self.getc
|
||||||
input = getwch
|
check_input_event
|
||||||
meta = (@@GetKeyState.call(VK_LMENU) & 0x80) != 0
|
@@output_buf.shift
|
||||||
control = (@@GetKeyState.call(VK_CONTROL) & 0x80) != 0
|
|
||||||
shift = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0
|
|
||||||
force_enter = !input.instance_of?(Array) && (control or shift) && input == 0x0D
|
|
||||||
if force_enter
|
|
||||||
# It's treated as Meta+Enter on Windows
|
|
||||||
@@output_buf.push("\e".ord)
|
|
||||||
@@output_buf.push(input)
|
|
||||||
else
|
|
||||||
case input
|
|
||||||
when 0x00
|
|
||||||
meta = false
|
|
||||||
@@output_buf.push(input)
|
|
||||||
input = getwch
|
|
||||||
@@output_buf.push(*input)
|
|
||||||
when 0xE0
|
|
||||||
@@output_buf.push(input)
|
|
||||||
input = getwch
|
|
||||||
@@output_buf.push(*input)
|
|
||||||
when 0x03
|
|
||||||
@@output_buf.push(input)
|
|
||||||
else
|
|
||||||
@@output_buf.push(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if meta
|
|
||||||
"\e".ord
|
|
||||||
else
|
|
||||||
@@output_buf.shift
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.ungetc(c)
|
def self.ungetc(c)
|
||||||
|
|
|
@ -730,6 +730,29 @@ begin
|
||||||
EOC
|
EOC
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_meta_key
|
||||||
|
start_terminal(50, 200, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
|
||||||
|
write("def ge\M-bho")
|
||||||
|
close
|
||||||
|
assert_screen(<<~EOC)
|
||||||
|
Multiline REPL.
|
||||||
|
prompt> def hoge
|
||||||
|
EOC
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_force_enter
|
||||||
|
start_terminal(50, 200, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
|
||||||
|
write("def hoge\nend\C-p\C-e")
|
||||||
|
write("\M-\x0D")
|
||||||
|
close
|
||||||
|
assert_screen(<<~EOC)
|
||||||
|
Multiline REPL.
|
||||||
|
prompt> def hoge
|
||||||
|
prompt>
|
||||||
|
prompt> end
|
||||||
|
EOC
|
||||||
|
end
|
||||||
|
|
||||||
private def write_inputrc(content)
|
private def write_inputrc(content)
|
||||||
File.open(@inputrc_file, 'w') do |f|
|
File.open(@inputrc_file, 'w') do |f|
|
||||||
f.write content
|
f.write content
|
||||||
|
|
Loading…
Reference in a new issue