Class: Reline::Windows
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
IO
|
|
Instance Chain:
self,
IO
|
|
Inherits: |
Reline::IO
|
Defined in: | lib/reline/io/windows.rb |
Constant Summary
-
CAPSLOCK_ON =
# File 'lib/reline/io/windows.rb', line 1370x0080
-
ENABLE_VIRTUAL_TERMINAL_PROCESSING =
# File 'lib/reline/io/windows.rb', line 1604
-
ENHANCED_KEY =
# File 'lib/reline/io/windows.rb', line 1380x0100
-
FILE_NAME_INFO =
# File 'lib/reline/io/windows.rb', line 1592
-
FILE_TYPE_PIPE =
# File 'lib/reline/io/windows.rb', line 1580x0003
-
KEY_EVENT =
# File 'lib/reline/io/windows.rb', line 1340x01
-
KEY_MAP =
# File 'lib/reline/io/windows.rb', line 211[ # It's treated as Meta+Enter on Windows. [ { control_keys: :CTRL, virtual_key_code: 0x0D }, "\e\r".bytes ], [ { control_keys: :SHIFT, virtual_key_code: 0x0D }, "\e\r".bytes ], # It's treated as Meta+Space on Windows. [ { control_keys: :CTRL, char_code: 0x20 }, "\e ".bytes ], # Emulate getwch() key sequences. [ { control_keys: [], virtual_key_code: VK_UP }, [0, 72] ], [ { control_keys: [], virtual_key_code: VK_DOWN }, [0, 80] ], [ { control_keys: [], virtual_key_code: VK_RIGHT }, [0, 77] ], [ { control_keys: [], virtual_key_code: VK_LEFT }, [0, 75] ], [ { control_keys: [], virtual_key_code: VK_DELETE }, [0, 83] ], [ { control_keys: [], virtual_key_code: VK_HOME }, [0, 71] ], [ { control_keys: [], virtual_key_code: VK_END }, [0, 79] ], # Emulate ANSI key sequence. [ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ], ]
-
LEFT_ALT_PRESSED =
# File 'lib/reline/io/windows.rb', line 1390x0002
-
LEFT_CTRL_PRESSED =
# File 'lib/reline/io/windows.rb', line 1400x0008
-
NUMLOCK_ON =
# File 'lib/reline/io/windows.rb', line 1410x0020
-
RIGHT_ALT_PRESSED =
# File 'lib/reline/io/windows.rb', line 1420x0001
-
RIGHT_CTRL_PRESSED =
# File 'lib/reline/io/windows.rb', line 1430x0004
-
SCROLLLOCK_ON =
# File 'lib/reline/io/windows.rb', line 1440x0040
-
SHIFT_PRESSED =
# File 'lib/reline/io/windows.rb', line 1450x0010
-
STD_INPUT_HANDLE =
# File 'lib/reline/io/windows.rb', line 156-10
-
STD_OUTPUT_HANDLE =
# File 'lib/reline/io/windows.rb', line 157-11
-
VK_CONTROL =
# File 'lib/reline/io/windows.rb', line 1300x11
-
VK_DELETE =
# File 'lib/reline/io/windows.rb', line 1540x2E
-
VK_DIVIDE =
# File 'lib/reline/io/windows.rb', line 1320x6F
-
VK_DOWN =
# File 'lib/reline/io/windows.rb', line 1530x28
-
VK_END =
# File 'lib/reline/io/windows.rb', line 1480x23
-
VK_HOME =
# File 'lib/reline/io/windows.rb', line 1490x24
-
VK_LEFT =
# File 'lib/reline/io/windows.rb', line 1500x25
-
VK_LMENU =
# File 'lib/reline/io/windows.rb', line 1290xA4
-
VK_MENU =
ALT key
0x12
-
VK_RETURN =
# File 'lib/reline/io/windows.rb', line 1270x0D
-
VK_RIGHT =
# File 'lib/reline/io/windows.rb', line 1520x27
-
VK_SHIFT =
# File 'lib/reline/io/windows.rb', line 1310x10
-
VK_TAB =
# File 'lib/reline/io/windows.rb', line 1470x09
-
VK_UP =
# File 'lib/reline/io/windows.rb', line 1510x26
-
WINDOW_BUFFER_SIZE_EVENT =
# File 'lib/reline/io/windows.rb', line 1350x04
IO
- Inherited
Class Method Summary
Instance Attribute Summary
- #empty_buffer? ⇒ Boolean readonly
- #in_pasting? ⇒ Boolean readonly
- #win? ⇒ Boolean readonly
- #win_legacy_console? ⇒ Boolean readonly
IO
- Inherited
Instance Method Summary
- #check_input_event
- #clear_screen
- #cursor_pos
- #deprep(otio)
- #encoding
- #erase_after_cursor
- #get_console_screen_buffer_info
- #get_screen_size
- #getc(_timeout_second)
- #hide_cursor
- #move_cursor_column(val)
- #move_cursor_down(val)
- #move_cursor_up(val)
-
#msys_tty?(io = @hConsoleInputHandle) ⇒ Boolean
if @legacy_console setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING) @legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0) end.
- #prep
- #process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
- #scroll_down(val)
- #set_default_key_bindings(config)
- #set_screen_size(rows, columns)
- #set_winch_handler(&handler)
- #show_cursor
- #ungetc(c)
- #with_raw_input
-
#call_with_console_handle(win32func, *args)
private
Calling Win32API with console handle is reported to fail after executing some external command.
- #getconsolemode private
- #setconsolemode(mode) private
IO
- Inherited
Constructor Details
.new ⇒ Windows
# File 'lib/reline/io/windows.rb', line 4
def initialize @input_buf = [] @output_buf = [] @output = STDOUT @hsg = nil @getwch = Win32API.new('msvcrt', '_getwch', [], 'I') @kbhit = Win32API.new('msvcrt', '_kbhit', [], 'I') @GetKeyState = Win32API.new('user32', 'GetKeyState', ['L'], 'L') @GetConsoleScreenBufferInfo = Win32API.new('kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L') @SetConsoleCursorPosition = Win32API.new('kernel32', 'SetConsoleCursorPosition', ['L', 'L'], 'L') @GetStdHandle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L') @FillConsoleOutputCharacter = Win32API.new('kernel32', 'FillConsoleOutputCharacter', ['L', 'L', 'L', 'L', 'P'], 'L') @ScrollConsoleScreenBuffer = Win32API.new('kernel32', 'ScrollConsoleScreenBuffer', ['L', 'P', 'P', 'L', 'P'], 'L') @hConsoleHandle = @GetStdHandle.call(STD_OUTPUT_HANDLE) @hConsoleInputHandle = @GetStdHandle.call(STD_INPUT_HANDLE) @GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['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') @SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L') @GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L') @SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L') @WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L') @legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 end
Instance Attribute Details
#empty_buffer? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/reline/io/windows.rb', line 323
def empty_buffer? if not @output_buf.empty? false elsif @kbhit.call == 0 true else false end end
#in_pasting? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/reline/io/windows.rb', line 319
def in_pasting? not empty_buffer? end
#win? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/reline/io/windows.rb', line 38
def win? true end
#win_legacy_console? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/reline/io/windows.rb', line 42
def win_legacy_console? @legacy_console end
Instance Method Details
#call_with_console_handle(win32func, *args) (private)
Calling Win32API with console handle is reported to fail after executing some external command. We need to refresh console handle and retry the call again.
# File 'lib/reline/io/windows.rb', line 164
private def call_with_console_handle(win32func, *args) val = win32func.call(@hConsoleHandle, *args) return val if val != 0 @hConsoleHandle = @GetStdHandle.call(STD_OUTPUT_HANDLE) win32func.call(@hConsoleHandle, *args) end
#check_input_event
[ GitHub ]# File 'lib/reline/io/windows.rb', line 269
def check_input_event num_of_events = 0.chr * 8 while @output_buf.empty? Reline.core.line_editor.handle_signal if @WaitForSingleObject.(@hConsoleInputHandle, 100) != 0 # max 0.1 sec # prevent for background consolemode change @legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 next end next if @GetNumberOfConsoleInputEvents.(@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack1('L') == 0 input_records = 0.chr * 20 * 80 read_event = 0.chr * 4 if @ReadConsoleInputW.(@hConsoleInputHandle, input_records, 80, read_event) != 0 read_events = read_event.unpack1('L') 0.upto(read_events) do |idx| input_record = input_records[idx * 20, 20] event = input_record[0, 2].unpack1('s*') case event when WINDOW_BUFFER_SIZE_EVENT @winch_handler.() when KEY_EVENT key_down = input_record[4, 4].unpack1('l*') repeat_count = input_record[8, 2].unpack1('s*') virtual_key_code = input_record[10, 2].unpack1('s*') virtual_scan_code = input_record[12, 2].unpack1('s*') char_code = input_record[14, 2].unpack1('S*') control_key_state = input_record[16, 2].unpack1('S*') 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
#clear_screen
[ GitHub ]# File 'lib/reline/io/windows.rb', line 429
def clear_screen if @legacy_console return unless csbi = get_console_screen_buffer_info buffer_width, _buffer_lines, attributes, window_top, window_bottom = csbi.unpack('ss@8S@12sx2s') fill_length = buffer_width * (window_bottom - window_top + 1) screen_topleft = window_top * 65536 written = 0.chr * 4 call_with_console_handle(@FillConsoleOutputCharacter, 0x20, fill_length, screen_topleft, written) call_with_console_handle(@FillConsoleOutputAttribute, attributes, fill_length, screen_topleft, written) call_with_console_handle(@SetConsoleCursorPosition, screen_topleft) else @output.write "\e[2J" "\e[H" end end
#cursor_pos
[ GitHub ]#deprep(otio)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 471
def deprep(otio) # do nothing end
#encoding
[ GitHub ]# File 'lib/reline/io/windows.rb', line 34
def encoding Encoding::UTF_8 end
#erase_after_cursor
[ GitHub ]# File 'lib/reline/io/windows.rb', line 393
def erase_after_cursor return unless csbi = get_console_screen_buffer_info attributes = csbi[8, 2].unpack1('S') cursor = csbi[4, 4].unpack1('L') written = 0.chr * 4 call_with_console_handle(@FillConsoleOutputCharacter, 0x20, get_screen_size.last - cursor_pos.x, cursor, written) call_with_console_handle(@FillConsoleOutputAttribute, attributes, get_screen_size.last - cursor_pos.x, cursor, written) end
#get_console_screen_buffer_info
[ GitHub ]# File 'lib/reline/io/windows.rb', line 333
def get_console_screen_buffer_info # CONSOLE_SCREEN_BUFFER_INFO # [ 0,2] dwSize.X # [ 2,2] dwSize.Y # [ 4,2] dwCursorPositions.X # [ 6,2] dwCursorPositions.Y # [ 8,2] wAttributes # [10,2] srWindow.Left # [12,2] srWindow.Top # [14,2] srWindow.Right # [16,2] srWindow.Bottom # [18,2] dwMaximumWindowSize.X # [20,2] dwMaximumWindowSize.Y csbi = 0.chr * 22 return if call_with_console_handle(@GetConsoleScreenBufferInfo, csbi) == 0 csbi end
#get_screen_size
[ GitHub ]# File 'lib/reline/io/windows.rb', line 351
def get_screen_size unless csbi = get_console_screen_buffer_info return [1, 1] end csbi[0, 4].unpack('SS').reverse end
#getc(_timeout_second)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 310
def getc(_timeout_second) check_input_event @output_buf.shift end
#getconsolemode (private)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 172
private def getconsolemode mode = "\000\000\000\000" call_with_console_handle(@GetConsoleMode, mode) mode.unpack1('L') end
#hide_cursor
[ GitHub ]# File 'lib/reline/io/windows.rb', line 448
def hide_cursor size = 100 visible = 0 # 0 means false cursor_info = [size, visible].pack('Li') call_with_console_handle(@SetConsoleCursorInfo, cursor_info) end
#move_cursor_column(val)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 367
def move_cursor_column(val) call_with_console_handle(@SetConsoleCursorPosition, cursor_pos.y * 65536 + val) end
#move_cursor_down(val)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 381
def move_cursor_down(val) if val > 0 return unless csbi = get_console_screen_buffer_info screen_height = get_screen_size.first y = cursor_pos.y + val y = screen_height - 1 if y > (screen_height - 1) call_with_console_handle(@SetConsoleCursorPosition, (cursor_pos.y + val) * 65536 + cursor_pos.x) elsif val < 0 move_cursor_up(-val) end end
#move_cursor_up(val)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 371
def move_cursor_up(val) if val > 0 y = cursor_pos.y - val y = 0 if y < 0 call_with_console_handle(@SetConsoleCursorPosition, y * 65536 + cursor_pos.x) elsif val < 0 move_cursor_down(-val) end end
#msys_tty?(io = @hConsoleInputHandle) ⇒ Boolean
if @legacy_console
setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
end
# File 'lib/reline/io/windows.rb', line 187
def msys_tty?(io = @hConsoleInputHandle) # check if fd is a pipe if @GetFileType.call(io) != FILE_TYPE_PIPE return false end bufsize = 1024 p_buffer = "\0" * bufsize res = @GetFileInformationByHandleEx.call(io, FILE_NAME_INFO, p_buffer, bufsize - 2) return false if res == 0 # get pipe name: p_buffer layout is: # struct _FILE_NAME_INFO { # DWORD FileNameLength; # WCHAR FileName[1]; # } FILE_NAME_INFO len = p_buffer[0, 4].unpack1("L") name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace) # Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX') # or a cygwin pty pipe ('\cygwin-XXXX-ptyN-XX') name =~ /(msys-|cygwin-).*-pty/ ? true : false end
#prep
[ GitHub ]# File 'lib/reline/io/windows.rb', line 466
def prep # do nothing nil end
#process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 232
def process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state) # high-surrogate if 0xD800 <= char_code and char_code <= 0xDBFF @hsg = char_code return end # low-surrogate if 0xDC00 <= char_code and char_code <= 0xDFFF if @hsg char_code = 0x10000 + (@hsg - 0xD800) * 0x400 + char_code - 0xDC00 @hsg = nil else # no high-surrogate. ignored. return end else # ignore high-surrogate without low-surrogate if there @hsg = nil end key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state) match = KEY_MAP.find { |args,| key.matches?(**args) } unless match.nil? @output_buf.concat(match.last) return end # no char, only control keys return if key.char_code == 0 and key.control_keys.any? @output_buf.push("\e".ord) if key.control_keys.include?(:ALT) and !key.control_keys.include?(:CTRL) @output_buf.concat(key.char.bytes) end
#scroll_down(val)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 402
def scroll_down(val) return if val < 0 return unless csbi = get_console_screen_buffer_info buffer_width, buffer_lines, x, y, attributes, window_left, window_top, window_bottom = csbi.unpack('ssssSssx2s') screen_height = window_bottom - window_top + 1 val = screen_height if val > screen_height if @legacy_console || window_left != 0 # unless ENABLE_VIRTUAL_TERMINAL, # if srWindow.Left != 0 then it's conhost.exe hosted console # and puts "\n" causes horizontal scroll. its glitch. # FYI irb write from culumn 1, so this gives no gain. scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4') destination_origin = 0 # y * 65536 + x fill = [' '.ord, attributes].pack('SS') call_with_console_handle(@ScrollConsoleScreenBuffer, scroll_rectangle, nil, destination_origin, fill) else origin_x = x + 1 origin_y = y - window_top + 1 @output.write [ (origin_y != screen_height) ? "\e[#{screen_height};H" : nil, "\n" * val, (origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil ].join end end
#set_default_key_bindings(config)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 46
def set_default_key_bindings(config) { [224, 72] => :ed_prev_history, # ↑ [224, 80] => :ed_next_history, # ↓ [224, 77] => :ed_next_char, # → [224, 75] => :ed_prev_char, # ← [224, 83] => :key_delete, # Del [224, 71] => :ed_move_to_beg, # Home [224, 79] => :ed_move_to_end, # End [ 0, 41] => :ed_unassigned, # input method on/off [ 0, 72] => :ed_prev_history, # ↑ [ 0, 80] => :ed_next_history, # ↓ [ 0, 77] => :ed_next_char, # → [ 0, 75] => :ed_prev_char, # ← [ 0, 83] => :key_delete, # Del [ 0, 71] => :ed_move_to_beg, # Home [ 0, 79] => :ed_move_to_end # End }.each_pair do |key, func| config.add_default_key_binding_by_keymap(:emacs, key, func) config.add_default_key_binding_by_keymap(:vi_insert, key, func) config.add_default_key_binding_by_keymap(:vi_command, key, func) end { [27, 32] => :em_set_mark, # M-<space> [24, 24] => :em_exchange_mark, # C-x C-x }.each_pair do |key, func| config.add_default_key_binding_by_keymap(:emacs, key, func) end # Emulate ANSI key sequence. { [27, 91, 90] => :completion_journey_up, # S-Tab }.each_pair do |key, func| config.add_default_key_binding_by_keymap(:emacs, key, func) config.add_default_key_binding_by_keymap(:vi_insert, key, func) end end
#set_screen_size(rows, columns)
# File 'lib/reline/io/windows.rb', line 444
def set_screen_size(rows, columns) raise NotImplementedError end
#set_winch_handler(&handler)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 462
def set_winch_handler(&handler) @winch_handler = handler end
#setconsolemode(mode) (private)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 178
private def setconsolemode(mode) call_with_console_handle(@SetConsoleMode, mode) end
#show_cursor
[ GitHub ]# File 'lib/reline/io/windows.rb', line 455
def show_cursor size = 100 visible = 1 # 1 means true cursor_info = [size, visible].pack('Li') call_with_console_handle(@SetConsoleCursorInfo, cursor_info) end
#ungetc(c)
[ GitHub ]# File 'lib/reline/io/windows.rb', line 315
def ungetc(c) @output_buf.unshift(c) end
#with_raw_input
[ GitHub ]# File 'lib/reline/io/windows.rb', line 306
def with_raw_input yield end