def self.check_input_event
num_of_events = 0.chr * 8
while @@output_buf.empty?
Reline.core.line_editor.resize
if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
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)
def self.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
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written)
@@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft)
@@output.write "\e[2J" "\e[H"
def self.cursor_pos
unless csbi = get_console_screen_buffer_info
return Reline::CursorPos.new(0, 0)
x = csbi[4, 2].unpack1('s')
y = csbi[6, 2].unpack1('s')
Reline::CursorPos.new(x, y)
def self.empty_buffer?
if not @@output_buf.empty?
false
elsif @@kbhit.call == 0
false
def self.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
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written)
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, get_screen_size.last - cursor_pos.x, cursor, written)
def self.get_console_screen_buffer_info
csbi = 0.chr * 22
return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
def self.get_screen_size
unless csbi = get_console_screen_buffer_info
return [1, 1]
csbi[0, 4].unpack('SS').reverse
visible = 0
cursor_info = [size, visible].pack('Li')
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
def self.move_cursor_column(val)
@@SetConsoleCursorPosition.call(@@hConsoleHandle, cursor_pos.y * 65536 + val)
def self.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)
@@SetConsoleCursorPosition.call(@@hConsoleHandle, (cursor_pos.y + val) * 65536 + cursor_pos.x)
elsif val < 0
move_cursor_up(-val)
y = cursor_pos.y - val
y = 0 if y < 0
@@SetConsoleCursorPosition.call(@@hConsoleHandle, y * 65536 + cursor_pos.x)
elsif val < 0
move_cursor_down(-val)
def self.msys_tty?(io = @@hConsoleInputHandle)
if @@GetFileType.call(io) != FILE_TYPE_PIPE
return false
bufsize = 1024
p_buffer = "\0" * bufsize
res = @@GetFileInformationByHandleEx.call(io, FILE_NAME_INFO, p_buffer, bufsize - 2)
return false if res == 0
len = p_buffer[0, 4].unpack1("L")
name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
name =~ /(msys-|cygwin-).*-pty/ ? true : false
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
click to toggle source
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
if 0xD800 <= char_code and char_code <= 0xDBFF
@@hsg = char_code
return
if 0xDC00 <= char_code and char_code <= 0xDFFF
if @@hsg
char_code = 0x10000 + (@@hsg - 0xD800) * 0x400 + char_code - 0xDC00
@@hsg = nil
return
@@hsg = nil
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
return if key.char_code == 0 and key.control_keys.any?
@@output_buf.push("\e".ord) if key.control_keys.include?(:ALT)
@@output_buf.concat(key.char.bytes)
def self.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
scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4')
destination_origin = 0
fill = [' '.ord, attributes].pack('SS')
@@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
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
[224, 72] => :ed_prev_history,
[224, 80] => :ed_next_history,
[224, 77] => :ed_next_char,
[224, 75] => :ed_prev_char,
[224, 83] => :key_delete,
[224, 71] => :ed_move_to_beg,
[224, 79] => :ed_move_to_end,
[ 0, 41] => :ed_unassigned,
[ 0, 72] => :ed_prev_history,
[ 0, 80] => :ed_next_history,
[ 0, 77] => :ed_next_char,
[ 0, 75] => :ed_prev_char,
[ 0, 83] => :key_delete,
[ 0, 71] => :ed_move_to_beg,
[ 0, 79] => :ed_move_to_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)
[27, 32] => :em_set_mark,
[24, 24] => :em_exchange_mark,
}.each_pair do |key, func|
config.add_default_key_binding_by_keymap(:emacs, key, func)
[27, 91, 90] => :completion_journey_up,
}.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)
def self.set_screen_size(rows, columns)
raise NotImplementedError
def self.set_winch_handler(&handler)
@@winch_handler = handler
visible = 1
cursor_info = [size, visible].pack('Li')
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
def self.getconsolemode
mode = "\000\000\000\000"
@@GetConsoleMode.call(@@hConsoleHandle, mode)
mode.unpack1('L')
def self.setconsolemode(mode)
@@SetConsoleMode.call(@@hConsoleHandle, mode)