summaryrefslogtreecommitdiff
path: root/lib/reline
diff options
context:
space:
mode:
authoraycabta <aycabta@gmail.com>2021-04-16 19:59:36 +0900
committeraycabta <aycabta@gmail.com>2021-06-21 17:58:48 +0900
commitc59bbd86a6bd0f1ce8a7babf56feabeb41a7e675 (patch)
treecf2ace2e080c079faa272041faaa817e8377ba54 /lib/reline
parenta049dfd10a3b9ae24a1822e807398422e87d142f (diff)
[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
Diffstat (limited to 'lib/reline')
-rw-r--r--lib/reline/windows.rb136
1 files changed, 74 insertions, 62 deletions
diff --git a/lib/reline/windows.rb b/lib/reline/windows.rb
index c645458cfc..50cfe3f7df 100644
--- a/lib/reline/windows.rb
+++ b/lib/reline/windows.rb
@@ -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,78 +178,69 @@ class Reline::Windows
name =~ /(msys-|cygwin-).*-pty/ ? true : false
end
- def self.getwch
- unless @@input_buf.empty?
- return @@input_buf.shift
- 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
+ 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
- @@input_buf.shift
end
- def self.getc
+ def self.check_input_event
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
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
- if event == WINDOW_BUFFER_SIZE_EVENT
+ 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
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
- @@output_buf.shift
- end
+ end
+
+ def self.getc
+ check_input_event
+ @@output_buf.shift
end
def self.ungetc(c)