Class: Reline::Windows
Defined Under Namespace
Classes: KeyEventRecord, Win32API
Constant Summary collapse
- VK_RETURN =
0x0D
- VK_MENU =
ALT key
0x12
- VK_LMENU =
0xA4
- VK_CONTROL =
0x11
- VK_SHIFT =
0x10
- VK_DIVIDE =
0x6F
- 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_TAB =
0x09
- 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
- FILE_TYPE_PIPE =
0x0003
- FILE_NAME_INFO =
2
- ENABLE_WRAP_AT_EOL_OUTPUT =
2
- ENABLE_VIRTUAL_TERMINAL_PROCESSING =
4
- KEY_MAP =
[ # 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] ], ]
- ALTERNATIVE_CSBI =
[80, 24, 0, 0, 7, 0, 0, 79, 23].freeze
Constants inherited from IO
Instance Attribute Summary collapse
-
#output ⇒ Object
writeonly
Sets the attribute output.
Instance Method Summary collapse
- #buffered_output ⇒ Object
- #check_input_event ⇒ Object
- #clear_screen ⇒ Object
- #cursor_pos ⇒ Object
- #deprep(otio) ⇒ Object
- #disable_auto_linewrap(setting = true, &block) ⇒ Object
- #empty_buffer? ⇒ Boolean
- #encoding ⇒ Object
- #erase_after_cursor ⇒ Object
- #get_console_screen_buffer_info ⇒ Object
- #get_screen_size ⇒ Object
- #getc(_timeout_second) ⇒ Object
- #hide_cursor ⇒ Object
- #in_pasting? ⇒ Boolean
-
#initialize ⇒ Windows
constructor
A new instance of Windows.
- #move_cursor_column(val) ⇒ Object
- #move_cursor_down(val) ⇒ Object
- #move_cursor_up(val) ⇒ Object
-
#msys_tty?(io = @hConsoleInputHandle) ⇒ Boolean
if @legacy_console setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING) @legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0) end.
- #prep ⇒ Object
- #process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state) ⇒ Object
-
#scroll_down(x) ⇒ Object
This only works when the cursor is at the bottom of the scroll range For more details, see github.com/ruby/reline/pull/577#issuecomment-1646679623.
- #set_default_key_bindings(config) ⇒ Object
- #set_screen_size(rows, columns) ⇒ Object
- #set_winch_handler(&handler) ⇒ Object
- #show_cursor ⇒ Object
- #ungetc(c) ⇒ Object
- #win? ⇒ Boolean
- #win_legacy_console? ⇒ Boolean
- #with_raw_input ⇒ Object
- #write(string) ⇒ Object
Methods inherited from IO
decide_io_gate, #dumb?, #read_single_char, #reset_color_sequence
Constructor Details
#initialize ⇒ Windows
Returns a new instance of Windows.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/reline/io/windows.rb', line 7 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
#output=(value) ⇒ Object (writeonly)
Sets the attribute output
5 6 7 |
# File 'lib/reline/io/windows.rb', line 5 def output=(value) @output = value end |
Instance Method Details
#buffered_output ⇒ Object
317 318 319 |
# File 'lib/reline/io/windows.rb', line 317 def buffered_output yield end |
#check_input_event ⇒ Object
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/reline/io/windows.rb', line 272 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 ⇒ Object
424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/reline/io/windows.rb', line 424 def clear_screen if @legacy_console width, _, _, _, attributes, _, top, _, bottom = get_console_screen_buffer_info return unless width fill_length = width * (bottom - top + 1) screen_topleft = 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 ⇒ Object
373 374 375 376 |
# File 'lib/reline/io/windows.rb', line 373 def cursor_pos _, _, x, y, _, _, top, = get_console_screen_buffer_info || ALTERNATIVE_CSBI Reline::CursorPos.new(x, y - top) end |
#deprep(otio) ⇒ Object
466 467 468 |
# File 'lib/reline/io/windows.rb', line 466 def deprep(otio) # do nothing end |
#disable_auto_linewrap(setting = true, &block) ⇒ Object
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 |
# File 'lib/reline/io/windows.rb', line 470 def disable_auto_linewrap(setting = true, &block) mode = getconsolemode if 0 == (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) if block begin setconsolemode(mode & ~ENABLE_WRAP_AT_EOL_OUTPUT) block.call ensure setconsolemode(mode | ENABLE_WRAP_AT_EOL_OUTPUT) end else if setting setconsolemode(mode & ~ENABLE_WRAP_AT_EOL_OUTPUT) else setconsolemode(mode | ENABLE_WRAP_AT_EOL_OUTPUT) end end else block.call if block end end |
#empty_buffer? ⇒ Boolean
334 335 336 337 338 339 340 341 342 |
# File 'lib/reline/io/windows.rb', line 334 def empty_buffer? if not @output_buf.empty? false elsif @kbhit.call == 0 true else false end end |
#encoding ⇒ Object
37 38 39 |
# File 'lib/reline/io/windows.rb', line 37 def encoding Encoding::UTF_8 end |
#erase_after_cursor ⇒ Object
408 409 410 411 412 413 414 |
# File 'lib/reline/io/windows.rb', line 408 def erase_after_cursor width, _, x, y, attributes, = get_console_screen_buffer_info return unless x written = 0.chr * 4 call_with_console_handle(@FillConsoleOutputCharacter, 0x20, width - x, y * 65536 + x, written) call_with_console_handle(@FillConsoleOutputAttribute, attributes, width - x, y * 65536 + x, written) end |
#get_console_screen_buffer_info ⇒ Object
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/reline/io/windows.rb', line 344 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 if call_with_console_handle(@GetConsoleScreenBufferInfo, csbi) != 0 # returns [width, height, x, y, attributes, left, top, right, bottom] csbi.unpack("s9") else return nil end end |
#get_screen_size ⇒ Object
368 369 370 371 |
# File 'lib/reline/io/windows.rb', line 368 def get_screen_size width, _, _, _, _, _, top, _, bottom = get_console_screen_buffer_info || ALTERNATIVE_CSBI [bottom - top + 1, width] end |
#getc(_timeout_second) ⇒ Object
321 322 323 324 |
# File 'lib/reline/io/windows.rb', line 321 def getc(_timeout_second) check_input_event @output_buf.shift end |
#hide_cursor ⇒ Object
443 444 445 446 447 448 |
# File 'lib/reline/io/windows.rb', line 443 def hide_cursor size = 100 visible = 0 # 0 means false cursor_info = [size, visible].pack('Li') call_with_console_handle(@SetConsoleCursorInfo, cursor_info) end |
#in_pasting? ⇒ Boolean
330 331 332 |
# File 'lib/reline/io/windows.rb', line 330 def in_pasting? not empty_buffer? end |
#move_cursor_column(val) ⇒ Object
378 379 380 381 |
# File 'lib/reline/io/windows.rb', line 378 def move_cursor_column(val) _, _, _, y, = get_console_screen_buffer_info call_with_console_handle(@SetConsoleCursorPosition, y * 65536 + val) if y end |
#move_cursor_down(val) ⇒ Object
395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/reline/io/windows.rb', line 395 def move_cursor_down(val) if val > 0 _, _, x, y, _, _, top, _, bottom = get_console_screen_buffer_info return unless y screen_height = bottom - top y = (y - top) + val y = screen_height if y > screen_height call_with_console_handle(@SetConsoleCursorPosition, (y + top) * 65536 + x) elsif val < 0 move_cursor_up(-val) end end |
#move_cursor_up(val) ⇒ Object
383 384 385 386 387 388 389 390 391 392 393 |
# File 'lib/reline/io/windows.rb', line 383 def move_cursor_up(val) if val > 0 _, _, x, y, _, _, top, = get_console_screen_buffer_info return unless y y = (y - top) - val y = 0 if y < 0 call_with_console_handle(@SetConsoleCursorPosition, (y + top) * 65536 + 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
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/reline/io/windows.rb', line 190 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 ⇒ Object
461 462 463 464 |
# File 'lib/reline/io/windows.rb', line 461 def prep # do nothing nil end |
#process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state) ⇒ Object
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/reline/io/windows.rb', line 235 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.match?(**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(x) ⇒ Object
This only works when the cursor is at the bottom of the scroll range For more details, see github.com/ruby/reline/pull/577#issuecomment-1646679623
418 419 420 421 422 |
# File 'lib/reline/io/windows.rb', line 418 def scroll_down(x) return if x.zero? # We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576 @output.write "\n" * x end |
#set_default_key_bindings(config) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/reline/io/windows.rb', line 49 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, 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) ⇒ Object
439 440 441 |
# File 'lib/reline/io/windows.rb', line 439 def set_screen_size(rows, columns) raise NotImplementedError end |
#set_winch_handler(&handler) ⇒ Object
457 458 459 |
# File 'lib/reline/io/windows.rb', line 457 def set_winch_handler(&handler) @winch_handler = handler end |
#show_cursor ⇒ Object
450 451 452 453 454 455 |
# File 'lib/reline/io/windows.rb', line 450 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) ⇒ Object
326 327 328 |
# File 'lib/reline/io/windows.rb', line 326 def ungetc(c) @output_buf.unshift(c) end |
#win? ⇒ Boolean
41 42 43 |
# File 'lib/reline/io/windows.rb', line 41 def win? true end |
#win_legacy_console? ⇒ Boolean
45 46 47 |
# File 'lib/reline/io/windows.rb', line 45 def win_legacy_console? @legacy_console end |
#with_raw_input ⇒ Object
309 310 311 |
# File 'lib/reline/io/windows.rb', line 309 def with_raw_input yield end |
#write(string) ⇒ Object
313 314 315 |
# File 'lib/reline/io/windows.rb', line 313 def write(string) @output.write(string) end |