@@ -157,6 +157,7 @@ def call(*args)
157
157
STD_OUTPUT_HANDLE = -11
158
158
FILE_TYPE_PIPE = 0x0003
159
159
FILE_NAME_INFO = 2
160
+ ENABLE_WRAP_AT_EOL_OUTPUT = 2
160
161
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
161
162
162
163
# Calling Win32API with console handle is reported to fail after executing some external command.
@@ -170,7 +171,7 @@ def call(*args)
170
171
end
171
172
172
173
private def getconsolemode
173
- mode = " \000 \000 \000 \000 "
174
+ mode = + " \0 \0 \0 \0 "
174
175
call_with_console_handle ( @GetConsoleMode , mode )
175
176
mode . unpack1 ( 'L' )
176
177
end
@@ -344,94 +345,78 @@ def get_console_screen_buffer_info
344
345
# [18,2] dwMaximumWindowSize.X
345
346
# [20,2] dwMaximumWindowSize.Y
346
347
csbi = 0 . chr * 22
347
- return if call_with_console_handle ( @GetConsoleScreenBufferInfo , csbi ) == 0
348
- csbi
348
+ if call_with_console_handle ( @GetConsoleScreenBufferInfo , csbi ) != 0
349
+ # returns [width, height, x, y, attributes, left, top, right, bottom]
350
+ csbi . unpack ( "s9" )
351
+ else
352
+ return nil
353
+ end
349
354
end
350
355
356
+ ALTERNATIVE_CSBI = [ 80 , 24 , 0 , 0 , 7 , 0 , 0 , 79 , 23 ] . freeze
357
+
351
358
def get_screen_size
352
- unless csbi = get_console_screen_buffer_info
353
- return [ 1 , 1 ]
354
- end
355
- csbi [ 0 , 4 ] . unpack ( 'SS' ) . reverse
359
+ width , _ , _ , _ , _ , _ , top , _ , bottom = get_console_screen_buffer_info || ALTERNATIVE_CSBI
360
+ [ bottom - top + 1 , width ]
356
361
end
357
362
358
363
def cursor_pos
359
- unless csbi = get_console_screen_buffer_info
360
- return Reline ::CursorPos . new ( 0 , 0 )
361
- end
362
- x = csbi [ 4 , 2 ] . unpack1 ( 's' )
363
- y = csbi [ 6 , 2 ] . unpack1 ( 's' )
364
- Reline ::CursorPos . new ( x , y )
364
+ _ , _ , x , y , _ , _ , top , = get_console_screen_buffer_info || ALTERNATIVE_CSBI
365
+ Reline ::CursorPos . new ( x , y - top )
365
366
end
366
367
367
368
def move_cursor_column ( val )
368
- call_with_console_handle ( @SetConsoleCursorPosition , cursor_pos . y * 65536 + val )
369
+ _ , _ , _ , y , = get_console_screen_buffer_info
370
+ call_with_console_handle ( @SetConsoleCursorPosition , y * 65536 + val ) if y
369
371
end
370
372
371
373
def move_cursor_up ( val )
372
374
if val > 0
373
- y = cursor_pos . y - val
375
+ _ , _ , x , y , _ , _ , top , = get_console_screen_buffer_info
376
+ return unless y
377
+ y = ( y - top ) - val
374
378
y = 0 if y < 0
375
- call_with_console_handle ( @SetConsoleCursorPosition , y * 65536 + cursor_pos . x )
379
+ call_with_console_handle ( @SetConsoleCursorPosition , ( y + top ) * 65536 + x )
376
380
elsif val < 0
377
381
move_cursor_down ( -val )
378
382
end
379
383
end
380
384
381
385
def move_cursor_down ( val )
382
386
if val > 0
383
- return unless csbi = get_console_screen_buffer_info
384
- screen_height = get_screen_size . first
385
- y = cursor_pos . y + val
386
- y = screen_height - 1 if y > ( screen_height - 1 )
387
- call_with_console_handle ( @SetConsoleCursorPosition , ( cursor_pos . y + val ) * 65536 + cursor_pos . x )
387
+ _ , _ , x , y , _ , _ , top , _ , bottom = get_console_screen_buffer_info
388
+ return unless y
389
+ screen_height = bottom - top
390
+ y = ( y - top ) + val
391
+ y = screen_height if y > screen_height
392
+ call_with_console_handle ( @SetConsoleCursorPosition , ( y + top ) * 65536 + x )
388
393
elsif val < 0
389
394
move_cursor_up ( -val )
390
395
end
391
396
end
392
397
393
398
def erase_after_cursor
394
- return unless csbi = get_console_screen_buffer_info
395
- attributes = csbi [ 8 , 2 ] . unpack1 ( 'S' )
396
- cursor = csbi [ 4 , 4 ] . unpack1 ( 'L' )
399
+ width , _ , x , y , attributes , = get_console_screen_buffer_info
400
+ return unless x
397
401
written = 0 . chr * 4
398
- call_with_console_handle ( @FillConsoleOutputCharacter , 0x20 , get_screen_size . last - cursor_pos . x , cursor , written )
399
- call_with_console_handle ( @FillConsoleOutputAttribute , attributes , get_screen_size . last - cursor_pos . x , cursor , written )
400
- end
401
-
402
- def scroll_down ( val )
403
- return if val < 0
404
- return unless csbi = get_console_screen_buffer_info
405
- buffer_width , buffer_lines , x , y , attributes , window_left , window_top , window_bottom = csbi . unpack ( 'ssssSssx2s' )
406
- screen_height = window_bottom - window_top + 1
407
- val = screen_height if val > screen_height
408
-
409
- if @legacy_console || window_left != 0
410
- # unless ENABLE_VIRTUAL_TERMINAL,
411
- # if srWindow.Left != 0 then it's conhost.exe hosted console
412
- # and puts "\n" causes horizontal scroll. its glitch.
413
- # FYI irb write from culumn 1, so this gives no gain.
414
- scroll_rectangle = [ 0 , val , buffer_width , buffer_lines - val ] . pack ( 's4' )
415
- destination_origin = 0 # y * 65536 + x
416
- fill = [ ' ' . ord , attributes ] . pack ( 'SS' )
417
- call_with_console_handle ( @ScrollConsoleScreenBuffer , scroll_rectangle , nil , destination_origin , fill )
418
- else
419
- origin_x = x + 1
420
- origin_y = y - window_top + 1
421
- @output . write [
422
- ( origin_y != screen_height ) ? "\e [#{ screen_height } ;H" : nil ,
423
- "\n " * val ,
424
- ( origin_y != screen_height or !x . zero? ) ? "\e [#{ origin_y } ;#{ origin_x } H" : nil
425
- ] . join
426
- end
402
+ call_with_console_handle ( @FillConsoleOutputCharacter , 0x20 , width - x , y * 65536 + x , written )
403
+ call_with_console_handle ( @FillConsoleOutputAttribute , attributes , width - x , y * 65536 + x , written )
404
+ end
405
+
406
+ # This only works when the cursor is at the bottom of the scroll range
407
+ # For more details, see https://github.com/ruby/reline/pull/577#issuecomment-1646679623
408
+ def scroll_down ( x )
409
+ return if x . zero?
410
+ # We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
411
+ @output . write "\n " * x
427
412
end
428
413
429
414
def clear_screen
430
415
if @legacy_console
431
- return unless csbi = get_console_screen_buffer_info
432
- buffer_width , _buffer_lines , attributes , window_top , window_bottom = csbi . unpack ( 'ss@8S@12sx2s' )
433
- fill_length = buffer_width * ( window_bottom - window_top + 1 )
434
- screen_topleft = window_top * 65536
416
+ width , _ , _ , _ , attributes , _ , top , _ , bottom = get_console_screen_buffer_info
417
+ return unless width
418
+ fill_length = width * ( bottom - top + 1 )
419
+ screen_topleft = top * 65536
435
420
written = 0 . chr * 4
436
421
call_with_console_handle ( @FillConsoleOutputCharacter , 0x20 , fill_length , screen_topleft , written )
437
422
call_with_console_handle ( @FillConsoleOutputAttribute , attributes , fill_length , screen_topleft , written )
@@ -472,6 +457,28 @@ def deprep(otio)
472
457
# do nothing
473
458
end
474
459
460
+ def disable_auto_linewrap ( setting = true , &block )
461
+ mode = getconsolemode
462
+ if 0 == ( mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING )
463
+ if block
464
+ begin
465
+ setconsolemode ( mode & ~ENABLE_WRAP_AT_EOL_OUTPUT )
466
+ block . call
467
+ ensure
468
+ setconsolemode ( mode | ENABLE_WRAP_AT_EOL_OUTPUT )
469
+ end
470
+ else
471
+ if setting
472
+ setconsolemode ( mode & ~ENABLE_WRAP_AT_EOL_OUTPUT )
473
+ else
474
+ setconsolemode ( mode | ENABLE_WRAP_AT_EOL_OUTPUT )
475
+ end
476
+ end
477
+ else
478
+ block . call if block
479
+ end
480
+ end
481
+
475
482
class KeyEventRecord
476
483
477
484
attr_reader :virtual_key_code , :char_code , :control_key_state , :control_keys
0 commit comments