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_CONTROL = 0x11
|
||||
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_OUTPUT_HANDLE = -11
|
||||
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
||||
FILE_TYPE_PIPE = 0x0003
|
||||
FILE_NAME_INFO = 2
|
||||
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
||||
|
@ -105,7 +126,7 @@ class Reline::Windows
|
|||
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
||||
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
||||
@@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')
|
||||
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
||||
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
||||
|
@ -157,79 +178,70 @@ class Reline::Windows
|
|||
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
||||
end
|
||||
|
||||
def self.getwch
|
||||
unless @@input_buf.empty?
|
||||
return @@input_buf.shift
|
||||
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
||||
char = char_code.chr(Encoding::UTF_8)
|
||||
if char_code == 0x0D and control_key_state.anybits?(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | SHIFT_PRESSED)
|
||||
# It's treated as Meta+Enter on Windows.
|
||||
@@output_buf.push("\e".ord)
|
||||
@@output_buf.push(char_code)
|
||||
elsif control_key_state.anybits?(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
|
||||
@@output_buf.push("\e".ord)
|
||||
@@output_buf.concat(char.bytes)
|
||||
elsif control_key_state.anybits?(ENHANCED_KEY)
|
||||
case virtual_key_code # Emulate getwch() key sequences.
|
||||
when VK_END
|
||||
@@output_buf.push(0, 79)
|
||||
when VK_HOME
|
||||
@@output_buf.push(0, 71)
|
||||
when VK_LEFT
|
||||
@@output_buf.push(0, 75)
|
||||
when VK_UP
|
||||
@@output_buf.push(0, 72)
|
||||
when VK_RIGHT
|
||||
@@output_buf.push(0, 77)
|
||||
when VK_DOWN
|
||||
@@output_buf.push(0, 80)
|
||||
when VK_DELETE
|
||||
@@output_buf.push(0, 83)
|
||||
end
|
||||
elsif char_code == 0 and control_key_state != 0
|
||||
# unknown
|
||||
else
|
||||
@@output_buf.concat(char.bytes)
|
||||
end
|
||||
end
|
||||
|
||||
def self.check_input_event
|
||||
num_of_events = 0.chr * 8
|
||||
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
|
||||
read_event = 0.chr * 4
|
||||
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
||||
event = input_record[0, 2].unpack('s*').first
|
||||
case event
|
||||
when WINDOW_BUFFER_SIZE_EVENT
|
||||
@@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
|
||||
while @@kbhit.call == 0
|
||||
sleep(0.001)
|
||||
end
|
||||
until @@kbhit.call == 0
|
||||
ret = @@getwch.call
|
||||
if ret == 0 or ret == 0xE0
|
||||
@@input_buf << ret
|
||||
ret = @@getwch.call
|
||||
@@input_buf << ret
|
||||
return @@input_buf.shift
|
||||
end
|
||||
begin
|
||||
bytes = ret.chr(Encoding::UTF_8).bytes
|
||||
@@input_buf.push(*bytes)
|
||||
rescue Encoding::UndefinedConversionError
|
||||
@@input_buf << ret
|
||||
@@input_buf << @@getwch.call if ret == 224
|
||||
end
|
||||
end
|
||||
@@input_buf.shift
|
||||
end
|
||||
|
||||
def self.getc
|
||||
num_of_events = 0.chr * 8
|
||||
while @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) != 0 and num_of_events.unpack('L').first > 0
|
||||
input_record = 0.chr * 18
|
||||
read_event = 0.chr * 4
|
||||
if @@ReadConsoleInput.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
||||
event = input_record[0, 2].unpack('s*').first
|
||||
if event == WINDOW_BUFFER_SIZE_EVENT
|
||||
@@winch_handler.()
|
||||
end
|
||||
end
|
||||
end
|
||||
unless @@output_buf.empty?
|
||||
return @@output_buf.shift
|
||||
end
|
||||
input = getwch
|
||||
meta = (@@GetKeyState.call(VK_LMENU) & 0x80) != 0
|
||||
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
|
||||
check_input_event
|
||||
@@output_buf.shift
|
||||
end
|
||||
end
|
||||
|
||||
def self.ungetc(c)
|
||||
@@output_buf.unshift(c)
|
||||
|
|
|
@ -730,6 +730,29 @@ begin
|
|||
EOC
|
||||
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)
|
||||
File.open(@inputrc_file, 'w') do |f|
|
||||
f.write content
|
||||
|
|
Loading…
Reference in a new issue