module TTY::Screen

Used for detecting screen properties

@api public

Constants

DEFAULT_SIZE

Default terminal size

@api public

STDOUT_HANDLE
TIOCGWINSZ
TIOCGWINSZ_PPC
VERSION

Attributes

env[RW]
output[RW]

Specifies an output stream

@api public

Public Class Methods

cols()
Alias for: width
columns()
Alias for: width
height() click to toggle source
# File lib/tty/screen.rb, line 63
def height
  size[0]
end
Also aliased as: rows, lines
ioctl?(control, buf) click to toggle source

Check if ioctl can be called and the device is attached to terminal

@api private

# File lib/tty/screen.rb, line 172
def ioctl?(control, buf)
  @output.ioctl(control, buf) >= 0
rescue SystemCallError
  false
end
lines()
Alias for: height
private_module_function(name) click to toggle source

Helper to define private functions

# File lib/tty/screen.rb, line 11
def self.private_module_function(name)
  module_function(name)
  private_class_method(name)
end
rows()
Alias for: height
size() click to toggle source

Get terminal rows and columns

@return [Array[Integer, Integer]]

return rows & columns

@api public

# File lib/tty/screen.rb, line 39
def size
  size = size_from_java
  size ||= size_from_win_api
  size ||= size_from_ioctl
  size ||= size_from_io_console
  size ||= size_from_readline
  size ||= size_from_tput
  size ||= size_from_stty
  size ||= size_from_env
  size ||= size_from_ansicon
  size ||  DEFAULT_SIZE
end
size_from_ansicon() click to toggle source

Detect terminal size from Windows ANSICON

@api private

# File lib/tty/screen.rb, line 236
def size_from_ansicon
  return unless @env['ANSICON'] =~ /\((.*)x(.*)\)/
  size = [$2, $1].map(&:to_i)
  size if nonzero_column?(size[1])
end
size_from_env() click to toggle source

Detect terminal size from environment

After executing Ruby code if the user changes terminal dimensions during code runtime, the code won't be notified, and hence won't see the new dimensions reflected in its copy of LINES and COLUMNS environment variables.

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 226
def size_from_env
  return unless @env['COLUMNS'] =~ /^\d+$/
  size = [(@env['LINES'] || @env['ROWS']).to_i, @env['COLUMNS'].to_i]
  size if nonzero_column?(size[1])
end
size_from_io_console(verbose: nil) click to toggle source

Detect screen size by loading io/console lib

On Windows io_console falls back to reading environment variables. This means any user changes to the terminal size won't be reflected in the runtime of the Ruby app.

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 131
def size_from_io_console(verbose: nil)
  return if jruby?
  require 'io/console'

  begin
    if @output.tty? && IO.method_defined?(:winsize)
      size = @output.winsize
      size if nonzero_column?(size[1])
    end
  rescue Errno::EOPNOTSUPP
    # no support for winsize on output
  end
rescue LoadError
  warn 'no native io/console support or io-console gem' if verbose
end
size_from_ioctl() click to toggle source

Read terminal size from Unix ioctl

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 156
def size_from_ioctl
  return if jruby?
  return unless @output.respond_to?(:ioctl)

  format = 'SSSS'
  buffer = ([0] * format.size).pack(format)
  if ioctl?(TIOCGWINSZ, buffer) || ioctl?(TIOCGWINSZ_PPC, buffer)
    rows, cols, = buffer.unpack(format)[0..1]
    return [rows, cols] if nonzero_column?(cols)
  end
end
size_from_java(verbose: nil) click to toggle source

Determine terminal size on jruby using native Java libs

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 110
def size_from_java(verbose: nil)
  return unless jruby?
  require 'java'
  java_import 'jline.TerminalFactory'
  terminal = TerminalFactory.get
  size = [terminal.get_height, terminal.get_width]
  return size if nonzero_column?(size[1])
rescue
  warn 'failed to import java terminal package' if verbose
end
size_from_readline() click to toggle source

Detect screen size using Readline

@api private

# File lib/tty/screen.rb, line 182
def size_from_readline
  if defined?(Readline) && Readline.respond_to?(:get_screen_size)
    size = Readline.get_screen_size
    size if nonzero_column?(size[1])
  end
rescue NotImplementedError
end
size_from_stty() click to toggle source

Detect terminal size from stty utility

@api private

# File lib/tty/screen.rb, line 206
def size_from_stty
  return unless @output.tty?
  out = run_command('stty', 'size')
  return unless out
  size = out.split.map(&:to_i)
  size if nonzero_column?(size[1])
rescue IOError, SystemCallError
end
size_from_tput() click to toggle source

Detect terminal size from tput utility

@api private

# File lib/tty/screen.rb, line 194
def size_from_tput
  return unless @output.tty?
  lines = run_command('tput', 'lines').to_i
  cols  = run_command('tput', 'cols').to_i
  [lines, cols] if nonzero_column?(lines)
rescue IOError, SystemCallError
end
size_from_win_api(verbose: nil) click to toggle source

Determine terminal size with a Windows native API

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 80
def size_from_win_api(verbose: nil)
  require 'fiddle'

  kernel32 = Fiddle::Handle.new('kernel32')
  get_std_handle = Fiddle::Function.new(kernel32['GetStdHandle'],
                    [-Fiddle::TYPE_INT], Fiddle::TYPE_INT)
  get_console_buffer_info = Fiddle::Function.new(
    kernel32['GetConsoleScreenBufferInfo'],
    [Fiddle::TYPE_LONG, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)

  format        = 'SSSSSssssSS'
  buffer        = ([0] * format.size).pack(format)
  stdout_handle = get_std_handle.(STDOUT_HANDLE)

  get_console_buffer_info.(stdout_handle, buffer)
  _, _, _, _, _, left, top, right, bottom, = buffer.unpack(format)
  size = [bottom - top + 1, right - left + 1]
  return size if nonzero_column?(size[1] - 1)
rescue LoadError
  warn 'no native fiddle module found' if verbose
rescue Fiddle::DLError
  # non windows platform or no kernel32 lib
end
width() click to toggle source
# File lib/tty/screen.rb, line 53
def width
  size[1]
end
Also aliased as: columns, cols

Public Instance Methods

jruby?() click to toggle source
# File lib/tty/screen.rb, line 268
def jruby?
  RbConfig::CONFIG['ruby_install_name'] == 'jruby'
end
nonzero_column?(column) click to toggle source

Check if number is non zero

return [Boolean]

@api private

# File lib/tty/screen.rb, line 263
def nonzero_column?(column)
  column.to_i > 0
end
run_command(*args) click to toggle source

Runs command silently capturing the output

@api private

# File lib/tty/screen.rb, line 246
def run_command(*args)
  require 'tempfile'
  out = Tempfile.new('tty-screen')
  result = system(*args, out: out.path, err: File::NULL)
  return if result.nil?
  out.rewind
  out.read
ensure
  out.close if out
end

Private Instance Methods

cols()
Alias for: width
columns()
Alias for: width
height() click to toggle source
# File lib/tty/screen.rb, line 63
def height
  size[0]
end
Also aliased as: rows, lines
ioctl?(control, buf) click to toggle source

Check if ioctl can be called and the device is attached to terminal

@api private

# File lib/tty/screen.rb, line 172
def ioctl?(control, buf)
  @output.ioctl(control, buf) >= 0
rescue SystemCallError
  false
end
lines()
Alias for: height
rows()
Alias for: height
size() click to toggle source

Get terminal rows and columns

@return [Array[Integer, Integer]]

return rows & columns

@api public

# File lib/tty/screen.rb, line 39
def size
  size = size_from_java
  size ||= size_from_win_api
  size ||= size_from_ioctl
  size ||= size_from_io_console
  size ||= size_from_readline
  size ||= size_from_tput
  size ||= size_from_stty
  size ||= size_from_env
  size ||= size_from_ansicon
  size ||  DEFAULT_SIZE
end
size_from_ansicon() click to toggle source

Detect terminal size from Windows ANSICON

@api private

# File lib/tty/screen.rb, line 236
def size_from_ansicon
  return unless @env['ANSICON'] =~ /\((.*)x(.*)\)/
  size = [$2, $1].map(&:to_i)
  size if nonzero_column?(size[1])
end
size_from_env() click to toggle source

Detect terminal size from environment

After executing Ruby code if the user changes terminal dimensions during code runtime, the code won't be notified, and hence won't see the new dimensions reflected in its copy of LINES and COLUMNS environment variables.

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 226
def size_from_env
  return unless @env['COLUMNS'] =~ /^\d+$/
  size = [(@env['LINES'] || @env['ROWS']).to_i, @env['COLUMNS'].to_i]
  size if nonzero_column?(size[1])
end
size_from_io_console(verbose: nil) click to toggle source

Detect screen size by loading io/console lib

On Windows io_console falls back to reading environment variables. This means any user changes to the terminal size won't be reflected in the runtime of the Ruby app.

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 131
def size_from_io_console(verbose: nil)
  return if jruby?
  require 'io/console'

  begin
    if @output.tty? && IO.method_defined?(:winsize)
      size = @output.winsize
      size if nonzero_column?(size[1])
    end
  rescue Errno::EOPNOTSUPP
    # no support for winsize on output
  end
rescue LoadError
  warn 'no native io/console support or io-console gem' if verbose
end
size_from_ioctl() click to toggle source

Read terminal size from Unix ioctl

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 156
def size_from_ioctl
  return if jruby?
  return unless @output.respond_to?(:ioctl)

  format = 'SSSS'
  buffer = ([0] * format.size).pack(format)
  if ioctl?(TIOCGWINSZ, buffer) || ioctl?(TIOCGWINSZ_PPC, buffer)
    rows, cols, = buffer.unpack(format)[0..1]
    return [rows, cols] if nonzero_column?(cols)
  end
end
size_from_java(verbose: nil) click to toggle source

Determine terminal size on jruby using native Java libs

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 110
def size_from_java(verbose: nil)
  return unless jruby?
  require 'java'
  java_import 'jline.TerminalFactory'
  terminal = TerminalFactory.get
  size = [terminal.get_height, terminal.get_width]
  return size if nonzero_column?(size[1])
rescue
  warn 'failed to import java terminal package' if verbose
end
size_from_readline() click to toggle source

Detect screen size using Readline

@api private

# File lib/tty/screen.rb, line 182
def size_from_readline
  if defined?(Readline) && Readline.respond_to?(:get_screen_size)
    size = Readline.get_screen_size
    size if nonzero_column?(size[1])
  end
rescue NotImplementedError
end
size_from_stty() click to toggle source

Detect terminal size from stty utility

@api private

# File lib/tty/screen.rb, line 206
def size_from_stty
  return unless @output.tty?
  out = run_command('stty', 'size')
  return unless out
  size = out.split.map(&:to_i)
  size if nonzero_column?(size[1])
rescue IOError, SystemCallError
end
size_from_tput() click to toggle source

Detect terminal size from tput utility

@api private

# File lib/tty/screen.rb, line 194
def size_from_tput
  return unless @output.tty?
  lines = run_command('tput', 'lines').to_i
  cols  = run_command('tput', 'cols').to_i
  [lines, cols] if nonzero_column?(lines)
rescue IOError, SystemCallError
end
size_from_win_api(verbose: nil) click to toggle source

Determine terminal size with a Windows native API

@return [nil, Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 80
def size_from_win_api(verbose: nil)
  require 'fiddle'

  kernel32 = Fiddle::Handle.new('kernel32')
  get_std_handle = Fiddle::Function.new(kernel32['GetStdHandle'],
                    [-Fiddle::TYPE_INT], Fiddle::TYPE_INT)
  get_console_buffer_info = Fiddle::Function.new(
    kernel32['GetConsoleScreenBufferInfo'],
    [Fiddle::TYPE_LONG, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)

  format        = 'SSSSSssssSS'
  buffer        = ([0] * format.size).pack(format)
  stdout_handle = get_std_handle.(STDOUT_HANDLE)

  get_console_buffer_info.(stdout_handle, buffer)
  _, _, _, _, _, left, top, right, bottom, = buffer.unpack(format)
  size = [bottom - top + 1, right - left + 1]
  return size if nonzero_column?(size[1] - 1)
rescue LoadError
  warn 'no native fiddle module found' if verbose
rescue Fiddle::DLError
  # non windows platform or no kernel32 lib
end
width() click to toggle source
# File lib/tty/screen.rb, line 53
def width
  size[1]
end
Also aliased as: columns, cols