Module: Rich::Win32Console
- Extended by:
- Fiddle::Importer
- Defined in:
- lib/rich/win32_console.rb
Constant Summary collapse
- STD_INPUT_HANDLE =
Standard handle constants
-10
- STD_OUTPUT_HANDLE =
-11
- STD_ERROR_HANDLE =
-12
- ENABLE_PROCESSED_INPUT =
Console mode flags
0x0001- ENABLE_LINE_INPUT =
0x0002- ENABLE_ECHO_INPUT =
0x0004- ENABLE_WINDOW_INPUT =
0x0008- ENABLE_MOUSE_INPUT =
0x0010- ENABLE_INSERT_MODE =
0x0020- ENABLE_QUICK_EDIT_MODE =
0x0040- ENABLE_EXTENDED_FLAGS =
0x0080- ENABLE_AUTO_POSITION =
0x0100- ENABLE_VIRTUAL_TERMINAL_INPUT =
0x0200- ENABLE_PROCESSED_OUTPUT =
Output mode flags
0x0001- ENABLE_WRAP_AT_EOL_OUTPUT =
0x0002- ENABLE_VIRTUAL_TERMINAL_PROCESSING =
0x0004- DISABLE_NEWLINE_AUTO_RETURN =
0x0008- ENABLE_LVB_GRID_WORLDWIDE =
0x0010- FOREGROUND_BLUE =
Console text attributes (foreground colors)
0x0001- FOREGROUND_GREEN =
0x0002- FOREGROUND_RED =
0x0004- FOREGROUND_INTENSITY =
0x0008- BACKGROUND_BLUE =
Console text attributes (background colors)
0x0010- BACKGROUND_GREEN =
0x0020- BACKGROUND_RED =
0x0040- BACKGROUND_INTENSITY =
0x0080- COMMON_LVB_LEADING_BYTE =
Additional text attributes
0x0100- COMMON_LVB_TRAILING_BYTE =
0x0200- COMMON_LVB_GRID_HORIZONTAL =
0x0400- COMMON_LVB_GRID_LVERTICAL =
0x0800- COMMON_LVB_GRID_RVERTICAL =
0x1000- COMMON_LVB_REVERSE_VIDEO =
0x4000- COMMON_LVB_UNDERSCORE =
0x8000- ANSI_TO_WINDOWS_FG =
ANSI color number to Windows console attribute mapping Maps ANSI color indices (0-15) to Windows FOREGROUND/BACKGROUND values
[ 0, # 0: Black FOREGROUND_RED, # 1: Red FOREGROUND_GREEN, # 2: Green FOREGROUND_RED | FOREGROUND_GREEN, # 3: Yellow FOREGROUND_BLUE, # 4: Blue FOREGROUND_RED | FOREGROUND_BLUE, # 5: Magenta FOREGROUND_GREEN | FOREGROUND_BLUE, # 6: Cyan FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, # 7: White FOREGROUND_INTENSITY, # 8: Bright Black (Gray) FOREGROUND_RED | FOREGROUND_INTENSITY, # 9: Bright Red FOREGROUND_GREEN | FOREGROUND_INTENSITY, # 10: Bright Green FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, # 11: Bright Yellow FOREGROUND_BLUE | FOREGROUND_INTENSITY, # 12: Bright Blue FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY, # 13: Bright Magenta FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, # 14: Bright Cyan FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY # 15: Bright White ].freeze
- ANSI_TO_WINDOWS_BG =
[ 0, # 0: Black BACKGROUND_RED, # 1: Red BACKGROUND_GREEN, # 2: Green BACKGROUND_RED | BACKGROUND_GREEN, # 3: Yellow BACKGROUND_BLUE, # 4: Blue BACKGROUND_RED | BACKGROUND_BLUE, # 5: Magenta BACKGROUND_GREEN | BACKGROUND_BLUE, # 6: Cyan BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, # 7: White BACKGROUND_INTENSITY, # 8: Bright Black (Gray) BACKGROUND_RED | BACKGROUND_INTENSITY, # 9: Bright Red BACKGROUND_GREEN | BACKGROUND_INTENSITY, # 10: Bright Green BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY, # 11: Bright Yellow BACKGROUND_BLUE | BACKGROUND_INTENSITY, # 12: Bright Blue BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY, # 13: Bright Magenta BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY, # 14: Bright Cyan BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY # 15: Bright White ].freeze
- INVALID_HANDLE_VALUE =
Sentinel returned by GetStdHandle on failure.
(1 << (Fiddle::SIZEOF_VOIDP * 8)) - 1
- CONSOLE_SCREEN_BUFFER_INFO_SIZE =
CONSOLE_SCREEN_BUFFER_INFO structure layout: typedef struct _CONSOLE_SCREEN_BUFFER_INFO
COORD dwSize; // 4 bytes (2x SHORT) COORD dwCursorPosition; // 4 bytes (2x SHORT) WORD wAttributes; // 2 bytes SMALL_RECT srWindow; // 8 bytes (4x SHORT) COORD dwMaximumWindowSize; // 4 bytes (2x SHORT)CONSOLE_SCREEN_BUFFER_INFO; Total: 22 bytes
22- CONSOLE_CURSOR_INFO_SIZE =
CONSOLE_CURSOR_INFO structure layout: typedef struct _CONSOLE_CURSOR_INFO
DWORD dwSize; // 4 bytes BOOL bVisible; // 4 bytesCONSOLE_CURSOR_INFO; Total: 8 bytes
8
Class Method Summary collapse
-
.ansi_to_windows_attributes(foreground: nil, background: nil) ⇒ Integer
Convert ANSI color number to Windows console attributes.
-
.clear_screen(handle = stdout_handle) ⇒ Boolean
Clear the entire screen.
-
.cursor_backward(columns = 1, handle = stdout_handle) ⇒ Boolean
Move cursor backward (left).
-
.cursor_down(lines = 1, handle = stdout_handle) ⇒ Boolean
Move cursor down.
-
.cursor_forward(columns = 1, handle = stdout_handle) ⇒ Boolean
Move cursor forward (right).
-
.cursor_to_column(column = 0, handle = stdout_handle) ⇒ Boolean
Move cursor to the beginning of the line.
-
.cursor_up(lines = 1, handle = stdout_handle) ⇒ Boolean
Move cursor up.
-
.disable_ansi! ⇒ Boolean
Disable virtual terminal (ANSI) processing.
-
.enable_ansi! ⇒ Boolean
Enable virtual terminal (ANSI) processing.
-
.erase_line(handle = stdout_handle) ⇒ Boolean
Erase from cursor to end of line.
-
.fill_output_attribute(attribute, length, x, y, handle = stdout_handle) ⇒ Integer?
Fill console output with an attribute.
-
.fill_output_character(char, length, x, y, handle = stdout_handle) ⇒ Integer?
Fill console output with a character.
-
.get_console_mode(handle = stdout_handle) ⇒ Integer?
Get the current console mode for a handle.
-
.get_cursor_position ⇒ Array<Integer>?
Get current cursor position.
-
.get_screen_buffer_info(handle = stdout_handle) ⇒ Hash?
Get console screen buffer info.
-
.get_size ⇒ Array<Integer>?
Get the console window dimensions.
-
.get_text_attributes ⇒ Integer?
Get current text attributes.
-
.hide_cursor(handle = stdout_handle) ⇒ Boolean
Hide the cursor.
-
.last_error ⇒ Integer
The calling thread’s last Win32 error code (0 if none).
-
.set_console_mode(mode, handle = stdout_handle) ⇒ Boolean
Set the console mode for a handle.
-
.set_cursor_position(x, y, handle = stdout_handle) ⇒ Boolean
Set cursor position.
-
.set_cursor_visibility(visible, handle = stdout_handle) ⇒ Boolean
Set cursor visibility.
-
.set_text_attribute(attributes, handle = stdout_handle) ⇒ Boolean
Set console text attributes (foreground/background colors).
-
.set_title(title) ⇒ Boolean
Set console window title.
-
.show_cursor(handle = stdout_handle) ⇒ Boolean
Show the cursor.
-
.stderr_handle ⇒ Integer?
Handle to stderr, or nil if unavailable.
-
.stdin_handle ⇒ Integer?
Handle to stdin, or nil if unavailable.
-
.stdout_handle ⇒ Integer?
Handle to stdout, or nil if unavailable.
-
.supports_ansi? ⇒ Boolean
Check if virtual terminal (ANSI) processing is supported.
-
.valid_handle(handle) ⇒ Object
Normalize a raw GetStdHandle result: NULL (0) means “no such handle” (e.g. output redirected to a pipe in a GUI process) and INVALID_HANDLE_VALUE means an error.
-
.windows? ⇒ Boolean
Whether the current platform is Windows.
-
.write_console(text, handle = stdout_handle) ⇒ Integer?
Write text to console (bypassing Ruby’s IO buffering).
Class Method Details
.ansi_to_windows_attributes(foreground: nil, background: nil) ⇒ Integer
Convert ANSI color number to Windows console attributes
607 608 609 610 611 612 |
# File 'lib/rich/win32_console.rb', line 607 def ansi_to_windows_attributes(foreground: nil, background: nil) attributes = 0 attributes |= ANSI_TO_WINDOWS_FG[foreground] if foreground && foreground < 16 attributes |= ANSI_TO_WINDOWS_BG[background] if background && background < 16 attributes end |
.clear_screen(handle = stdout_handle) ⇒ Boolean
Clear the entire screen
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
# File 'lib/rich/win32_console.rb', line 491 def clear_screen(handle = stdout_handle) return false unless windows? && handle info = get_screen_buffer_info(handle) return false unless info size = info[:size][:width] * info[:size][:height] attributes = info[:attributes] fill_output_character(" ", size, 0, 0, handle) fill_output_attribute(attributes, size, 0, 0, handle) set_cursor_position(0, 0, handle) true end |
.cursor_backward(columns = 1, handle = stdout_handle) ⇒ Boolean
Move cursor backward (left)
581 582 583 584 585 586 587 588 589 |
# File 'lib/rich/win32_console.rb', line 581 def cursor_backward(columns = 1, handle = stdout_handle) return false unless windows? pos = get_cursor_position return false unless pos new_x = [pos[0] - columns, 0].max set_cursor_position(new_x, pos[1], handle) end |
.cursor_down(lines = 1, handle = stdout_handle) ⇒ Boolean
Move cursor down
545 546 547 548 549 550 551 552 553 554 555 556 557 |
# File 'lib/rich/win32_console.rb', line 545 def cursor_down(lines = 1, handle = stdout_handle) return false unless windows? info = get_screen_buffer_info(handle) return false unless info pos = get_cursor_position return false unless pos max_y = info[:size][:height] - 1 new_y = [pos[1] + lines, max_y].min set_cursor_position(pos[0], new_y, handle) end |
.cursor_forward(columns = 1, handle = stdout_handle) ⇒ Boolean
Move cursor forward (right)
563 564 565 566 567 568 569 570 571 572 573 574 575 |
# File 'lib/rich/win32_console.rb', line 563 def cursor_forward(columns = 1, handle = stdout_handle) return false unless windows? info = get_screen_buffer_info(handle) return false unless info pos = get_cursor_position return false unless pos max_x = info[:size][:width] - 1 new_x = [pos[0] + columns, max_x].min set_cursor_position(new_x, pos[1], handle) end |
.cursor_to_column(column = 0, handle = stdout_handle) ⇒ Boolean
Move cursor to the beginning of the line
594 595 596 597 598 599 600 601 |
# File 'lib/rich/win32_console.rb', line 594 def cursor_to_column(column = 0, handle = stdout_handle) return false unless windows? pos = get_cursor_position return false unless pos set_cursor_position(column, pos[1], handle) end |
.cursor_up(lines = 1, handle = stdout_handle) ⇒ Boolean
Move cursor up
531 532 533 534 535 536 537 538 539 |
# File 'lib/rich/win32_console.rb', line 531 def cursor_up(lines = 1, handle = stdout_handle) return false unless windows? pos = get_cursor_position return false unless pos new_y = [pos[1] - lines, 0].max set_cursor_position(pos[0], new_y, handle) end |
.disable_ansi! ⇒ Boolean
Disable virtual terminal (ANSI) processing
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/rich/win32_console.rb', line 274 def disable_ansi! return false unless windows? handle = stdout_handle return false unless handle current_mode = get_console_mode(handle) return false unless current_mode new_mode = current_mode & ~ENABLE_VIRTUAL_TERMINAL_PROCESSING result = set_console_mode(new_mode, handle) @supports_ansi = !result if result result end |
.enable_ansi! ⇒ Boolean
Enable virtual terminal (ANSI) processing
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/rich/win32_console.rb', line 255 def enable_ansi! return true unless windows? # Already supported on Unix handle = stdout_handle return false unless handle current_mode = get_console_mode(handle) return false unless current_mode new_mode = current_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING result = set_console_mode(new_mode, handle) # Update cached value @supports_ansi = result result end |
.erase_line(handle = stdout_handle) ⇒ Boolean
Erase from cursor to end of line
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
# File 'lib/rich/win32_console.rb', line 510 def erase_line(handle = stdout_handle) return false unless windows? && handle info = get_screen_buffer_info(handle) return false unless info x = info[:cursor_position][:x] y = info[:cursor_position][:y] length = info[:size][:width] - x attributes = info[:attributes] fill_output_character(" ", length, x, y, handle) fill_output_attribute(attributes, length, x, y, handle) true end |
.fill_output_attribute(attribute, length, x, y, handle = stdout_handle) ⇒ Integer?
Fill console output with an attribute
410 411 412 413 414 415 416 417 418 419 420 |
# File 'lib/rich/win32_console.rb', line 410 def fill_output_attribute(attribute, length, x, y, handle = stdout_handle) return nil unless windows? && handle coord = ((y & 0xFFFF) << 16) | (x & 0xFFFF) written_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_LONG, Fiddle::RUBY_FREE) result = FillConsoleOutputAttribute(handle, attribute, length, coord, written_ptr) return nil if result == 0 written_ptr[0, Fiddle::SIZEOF_LONG].unpack1("L") end |
.fill_output_character(char, length, x, y, handle = stdout_handle) ⇒ Integer?
Fill console output with a character
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
# File 'lib/rich/win32_console.rb', line 386 def fill_output_character(char, length, x, y, handle = stdout_handle) return nil unless windows? && handle # COORD is two SHORTs marshalled as a packed DWORD; mask both fields. coord = ((y & 0xFFFF) << 16) | (x & 0xFFFF) written_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_LONG, Fiddle::RUBY_FREE) # cCharacter is a single UTF-16 code unit. Characters above U+FFFF # cannot be represented; mask to the low 16 bits to make the truncation # explicit rather than relying on Fiddle's silent narrowing. char_code = char.ord & 0xFFFF result = FillConsoleOutputCharacterW(handle, char_code, length, coord, written_ptr) return nil if result == 0 written_ptr[0, Fiddle::SIZEOF_LONG].unpack1("L") end |
.get_console_mode(handle = stdout_handle) ⇒ Integer?
Get the current console mode for a handle
217 218 219 220 221 222 223 224 225 |
# File 'lib/rich/win32_console.rb', line 217 def get_console_mode(handle = stdout_handle) return nil unless windows? && handle mode_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_LONG, Fiddle::RUBY_FREE) result = GetConsoleMode(handle, mode_ptr) return nil if result == 0 mode_ptr[0, Fiddle::SIZEOF_LONG].unpack1("L") end |
.get_cursor_position ⇒ Array<Integer>?
Get current cursor position
335 336 337 338 339 340 341 342 |
# File 'lib/rich/win32_console.rb', line 335 def get_cursor_position return nil unless windows? info = get_screen_buffer_info return nil unless info [info[:cursor_position][:x], info[:cursor_position][:y]] end |
.get_screen_buffer_info(handle = stdout_handle) ⇒ Hash?
Get console screen buffer info
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/rich/win32_console.rb', line 293 def get_screen_buffer_info(handle = stdout_handle) return nil unless windows? && handle buffer = Fiddle::Pointer.malloc(CONSOLE_SCREEN_BUFFER_INFO_SIZE, Fiddle::RUBY_FREE) result = GetConsoleScreenBufferInfo(handle, buffer) return nil if result == 0 data = buffer[0, CONSOLE_SCREEN_BUFFER_INFO_SIZE] # Unpack the structure values = data.unpack("s2 s2 S s4 s2") { size: { width: values[0], height: values[1] }, cursor_position: { x: values[2], y: values[3] }, attributes: values[4], window: { left: values[5], top: values[6], right: values[7], bottom: values[8] }, max_window_size: { width: values[9], height: values[10] } } end |
.get_size ⇒ Array<Integer>?
Get the console window dimensions
321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/rich/win32_console.rb', line 321 def get_size return nil unless windows? info = get_screen_buffer_info return nil unless info width = info[:window][:right] - info[:window][:left] + 1 height = info[:window][:bottom] - info[:window][:top] + 1 [width, height] end |
.get_text_attributes ⇒ Integer?
Get current text attributes
370 371 372 373 374 375 376 377 |
# File 'lib/rich/win32_console.rb', line 370 def get_text_attributes return nil unless windows? info = get_screen_buffer_info return nil unless info info[:attributes] end |
.hide_cursor(handle = stdout_handle) ⇒ Boolean
Hide the cursor
443 444 445 |
# File 'lib/rich/win32_console.rb', line 443 def hide_cursor(handle = stdout_handle) set_cursor_visibility(false, handle) end |
.last_error ⇒ Integer
Returns The calling thread’s last Win32 error code (0 if none).
194 195 196 197 198 |
# File 'lib/rich/win32_console.rb', line 194 def last_error return 0 unless windows? GetLastError() end |
.set_console_mode(mode, handle = stdout_handle) ⇒ Boolean
Set the console mode for a handle
231 232 233 234 235 |
# File 'lib/rich/win32_console.rb', line 231 def set_console_mode(mode, handle = stdout_handle) return false unless windows? && handle SetConsoleMode(handle, mode) != 0 end |
.set_cursor_position(x, y, handle = stdout_handle) ⇒ Boolean
Set cursor position
349 350 351 352 353 354 355 356 |
# File 'lib/rich/win32_console.rb', line 349 def set_cursor_position(x, y, handle = stdout_handle) return false unless windows? && handle # Pack COORD structure as DWORD (low word = X, high word = Y); mask # both fields so a stray high bit can't corrupt the other coordinate. coord = ((y & 0xFFFF) << 16) | (x & 0xFFFF) SetConsoleCursorPosition(handle, coord) != 0 end |
.set_cursor_visibility(visible, handle = stdout_handle) ⇒ Boolean
Set cursor visibility
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
# File 'lib/rich/win32_console.rb', line 451 def set_cursor_visibility(visible, handle = stdout_handle) return false unless windows? && handle # Get current cursor info buffer = Fiddle::Pointer.malloc(CONSOLE_CURSOR_INFO_SIZE, Fiddle::RUBY_FREE) result = GetConsoleCursorInfo(handle, buffer) return false if result == 0 # Modify visibility data = buffer[0, CONSOLE_CURSOR_INFO_SIZE].unpack("L L") cursor_size = data[0] buffer[0, CONSOLE_CURSOR_INFO_SIZE] = [cursor_size, visible ? 1 : 0].pack("L L") SetConsoleCursorInfo(handle, buffer) != 0 end |
.set_text_attribute(attributes, handle = stdout_handle) ⇒ Boolean
Set console text attributes (foreground/background colors)
362 363 364 365 366 |
# File 'lib/rich/win32_console.rb', line 362 def set_text_attribute(attributes, handle = stdout_handle) return false unless windows? && handle SetConsoleTextAttribute(handle, attributes) != 0 end |
.set_title(title) ⇒ Boolean
Set console window title
425 426 427 428 429 430 431 |
# File 'lib/rich/win32_console.rb', line 425 def set_title(title) return false unless windows? # Convert to UTF-16LE with null terminator wide_title = (title + "\0").encode("UTF-16LE") SetConsoleTitleW(Fiddle::Pointer[wide_title]) != 0 end |
.show_cursor(handle = stdout_handle) ⇒ Boolean
Show the cursor
436 437 438 |
# File 'lib/rich/win32_console.rb', line 436 def show_cursor(handle = stdout_handle) set_cursor_visibility(true, handle) end |
.stderr_handle ⇒ Integer?
Returns Handle to stderr, or nil if unavailable.
188 189 190 191 |
# File 'lib/rich/win32_console.rb', line 188 def stderr_handle return nil unless windows? @stderr_handle ||= valid_handle(GetStdHandle(STD_ERROR_HANDLE)) end |
.stdin_handle ⇒ Integer?
Returns Handle to stdin, or nil if unavailable.
182 183 184 185 |
# File 'lib/rich/win32_console.rb', line 182 def stdin_handle return nil unless windows? @stdin_handle ||= valid_handle(GetStdHandle(STD_INPUT_HANDLE)) end |
.stdout_handle ⇒ Integer?
Returns Handle to stdout, or nil if unavailable.
176 177 178 179 |
# File 'lib/rich/win32_console.rb', line 176 def stdout_handle return nil unless windows? @stdout_handle ||= valid_handle(GetStdHandle(STD_OUTPUT_HANDLE)) end |
.supports_ansi? ⇒ Boolean
Check if virtual terminal (ANSI) processing is supported
239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/rich/win32_console.rb', line 239 def supports_ansi? return @supports_ansi if defined?(@supports_ansi) unless windows? @supports_ansi = true # Unix terminals support ANSI return @supports_ansi end mode = get_console_mode return @supports_ansi = false if mode.nil? @supports_ansi = (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0 end |
.valid_handle(handle) ⇒ Object
Normalize a raw GetStdHandle result: NULL (0) means “no such handle” (e.g. output redirected to a pipe in a GUI process) and INVALID_HANDLE_VALUE means an error. Both become nil so callers and the ‘&& handle` guards short-circuit instead of issuing API calls against a bad handle. A failed lookup is intentionally NOT memoized.
205 206 207 208 209 210 211 212 |
# File 'lib/rich/win32_console.rb', line 205 def valid_handle(handle) return nil if handle.nil? value = handle.respond_to?(:to_i) ? handle.to_i : handle return nil if value.zero? || value == INVALID_HANDLE_VALUE handle end |
.windows? ⇒ Boolean
Returns Whether the current platform is Windows.
171 172 173 |
# File 'lib/rich/win32_console.rb', line 171 def windows? Gem.win_platform? end |
.write_console(text, handle = stdout_handle) ⇒ Integer?
Write text to console (bypassing Ruby’s IO buffering)
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/rich/win32_console.rb', line 471 def write_console(text, handle = stdout_handle) return nil unless windows? && handle wide_text = text.encode("UTF-16LE") # WriteConsoleW counts UTF-16 code UNITS, not Ruby characters (code # points). Characters outside the BMP (e.g. emoji) encode as 2 units, so # using text.length would truncate the tail. Derive the count from the # encoded buffer. char_count = wide_text.bytesize / 2 written_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_LONG, Fiddle::RUBY_FREE) result = WriteConsoleW(handle, Fiddle::Pointer[wide_text], char_count, written_ptr, nil) return nil if result == 0 written_ptr[0, Fiddle::SIZEOF_LONG].unpack1("L") end |