module TTY::Screen

Used for detecting screen properties

@api public

Constants

DEFAULT_SIZE

Default terminal size

@api public

STDOUT_HANDLE
TIOCGWINSZ
TIOCGWINSZ_PPC
TIOCGWINSZ_SOL
VERSION

Attributes

env[RW]

Holds the environment variables @api public

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 85
def height
  size[0]
end
Also aliased as: rows, lines
jruby?() click to toggle source
# File lib/tty/screen.rb, line 31
def jruby?; true end
lines()
Alias for: height
private_module_function(name) click to toggle source

Helper to define private functions

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

Get terminal rows and columns

@return [Array[Integer, Integer]]

return rows and columns

@api public

# File lib/tty/screen.rb, line 61
def size(verbose: false)
  size_from_java(verbose: verbose) ||
  size_from_win_api(verbose: verbose) ||
  size_from_ioctl ||
  size_from_io_console(verbose: verbose) ||
  size_from_readline(verbose: verbose) ||
  size_from_tput ||
  size_from_stty ||
  size_from_env ||
  size_from_ansicon ||
  size_from_default
end
size_from_ansicon() click to toggle source

Detect terminal size from Windows ANSICON

@api private

# File lib/tty/screen.rb, line 293
def size_from_ansicon
  return unless @env["ANSICON"] =~ /\((.*)x(.*)\)/

  size = [$2, $1].map(&:to_i)
  size if nonzero_column?(size[1])
end
size_from_default() click to toggle source

Default size for the terminal

@return [Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 100
def size_from_default
  DEFAULT_SIZE
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 282
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: false) 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 171
def size_from_io_console(verbose: false)
  require "io/console" unless IO.method_defined?(:winsize)

  return unless @output.tty? && @output.respond_to?(:winsize)

  size = @output.winsize
  size if nonzero_column?(size[1])
rescue Errno::EOPNOTSUPP
  # no support for winsize on output
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 195
def size_from_ioctl
  format = "SSSS"
  buffer = ([0] * format.size).pack(format)

  if ioctl?(TIOCGWINSZ, buffer) ||
     ioctl?(TIOCGWINSZ_PPC, buffer) ||
     ioctl?(TIOCGWINSZ_SOL, buffer)

    rows, cols, = buffer.unpack(format)[0..1]
    return [rows, cols] if nonzero_column?(cols)
  end
end
size_from_java(verbose: false) click to toggle source
# File lib/tty/screen.rb, line 147
def size_from_java(verbose: false)
  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(verbose: false) click to toggle source

Detect screen size using Readline

@api private

# File lib/tty/screen.rb, line 231
def size_from_readline(verbose: false)
  require "readline" unless defined?(::Readline)

  return unless ::Readline.respond_to?(:get_screen_size)

  size = ::Readline.get_screen_size
  size if nonzero_column?(size[1])
rescue LoadError
  warn "no readline gem" if verbose
rescue NotImplementedError
end
size_from_stty() click to toggle source

Detect terminal size from stty utility

@api private

# File lib/tty/screen.rb, line 261
def size_from_stty
  return unless @output.tty? && command_exist?("stty")

  out = run_command("stty", "size")
  return unless out

  size = out.split.map(&:to_i)
  size if nonzero_column?(size[1])
end
size_from_tput() click to toggle source

Detect terminal size from tput utility

@api private

# File lib/tty/screen.rb, line 247
def size_from_tput
  return unless @output.tty? && command_exist?("tput")

  lines = run_command("tput", "lines")
  return unless lines

  cols = run_command("tput", "cols")
  [lines.to_i, cols.to_i] if nonzero_column?(lines)
end
size_from_win_api(verbose: false) click to toggle source
# File lib/tty/screen.rb, line 113
def size_from_win_api(verbose: false)
  require "fiddle" unless defined?(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 75
def width
  size[1]
end
Also aliased as: columns, cols
windows?() click to toggle source
# File lib/tty/screen.rb, line 23
def windows?; true end

Public Instance Methods

command_exist?(command) click to toggle source

Check if command exists

@return [Boolean]

@api private

# File lib/tty/screen.rb, line 306
def command_exist?(command)
  exts = env.fetch("PATHEXT", "").split(::File::PATH_SEPARATOR)
  env.fetch("PATH", "").split(::File::PATH_SEPARATOR).any? do |dir|
    file = ::File.join(dir, command)
    ::File.exist?(file) || exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
  end
end
ioctl?(control, buf) click to toggle source

Check if ioctl can be called and any of the streams is attached to a terminal.

@return [Boolean]

True if any of the streams is attached to a terminal, false otherwise.

@api private

# File lib/tty/screen.rb, line 215
def ioctl?(control, buf)
  ($stdout.ioctl(control, buf) >= 0) ||
    ($stdin.ioctl(control, buf) >= 0) ||
    ($stderr.ioctl(control, buf) >= 0)
rescue SystemCallError
  false
end
nonzero_column?(column) click to toggle source

Check if number is non zero

return [Boolean]

@api private

# File lib/tty/screen.rb, line 330
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 318
def run_command(*args)
  %x(#{args.join(" ")})
rescue IOError, SystemCallError
  nil
end

Private Instance Methods

cols()
Alias for: width
columns()
Alias for: width
height() click to toggle source
# File lib/tty/screen.rb, line 85
def height
  size[0]
end
Also aliased as: rows, lines
jruby?() click to toggle source
# File lib/tty/screen.rb, line 31
def jruby?; true end
lines()
Alias for: height
rows()
Alias for: height
size(verbose: false) click to toggle source

Get terminal rows and columns

@return [Array[Integer, Integer]]

return rows and columns

@api public

# File lib/tty/screen.rb, line 61
def size(verbose: false)
  size_from_java(verbose: verbose) ||
  size_from_win_api(verbose: verbose) ||
  size_from_ioctl ||
  size_from_io_console(verbose: verbose) ||
  size_from_readline(verbose: verbose) ||
  size_from_tput ||
  size_from_stty ||
  size_from_env ||
  size_from_ansicon ||
  size_from_default
end
size_from_ansicon() click to toggle source

Detect terminal size from Windows ANSICON

@api private

# File lib/tty/screen.rb, line 293
def size_from_ansicon
  return unless @env["ANSICON"] =~ /\((.*)x(.*)\)/

  size = [$2, $1].map(&:to_i)
  size if nonzero_column?(size[1])
end
size_from_default() click to toggle source

Default size for the terminal

@return [Array[Integer, Integer]]

@api private

# File lib/tty/screen.rb, line 100
def size_from_default
  DEFAULT_SIZE
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 282
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: false) 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 171
def size_from_io_console(verbose: false)
  require "io/console" unless IO.method_defined?(:winsize)

  return unless @output.tty? && @output.respond_to?(:winsize)

  size = @output.winsize
  size if nonzero_column?(size[1])
rescue Errno::EOPNOTSUPP
  # no support for winsize on output
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 195
def size_from_ioctl
  format = "SSSS"
  buffer = ([0] * format.size).pack(format)

  if ioctl?(TIOCGWINSZ, buffer) ||
     ioctl?(TIOCGWINSZ_PPC, buffer) ||
     ioctl?(TIOCGWINSZ_SOL, buffer)

    rows, cols, = buffer.unpack(format)[0..1]
    return [rows, cols] if nonzero_column?(cols)
  end
end
size_from_java(verbose: false) click to toggle source
# File lib/tty/screen.rb, line 147
def size_from_java(verbose: false)
  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(verbose: false) click to toggle source

Detect screen size using Readline

@api private

# File lib/tty/screen.rb, line 231
def size_from_readline(verbose: false)
  require "readline" unless defined?(::Readline)

  return unless ::Readline.respond_to?(:get_screen_size)

  size = ::Readline.get_screen_size
  size if nonzero_column?(size[1])
rescue LoadError
  warn "no readline gem" if verbose
rescue NotImplementedError
end
size_from_stty() click to toggle source

Detect terminal size from stty utility

@api private

# File lib/tty/screen.rb, line 261
def size_from_stty
  return unless @output.tty? && command_exist?("stty")

  out = run_command("stty", "size")
  return unless out

  size = out.split.map(&:to_i)
  size if nonzero_column?(size[1])
end
size_from_tput() click to toggle source

Detect terminal size from tput utility

@api private

# File lib/tty/screen.rb, line 247
def size_from_tput
  return unless @output.tty? && command_exist?("tput")

  lines = run_command("tput", "lines")
  return unless lines

  cols = run_command("tput", "cols")
  [lines.to_i, cols.to_i] if nonzero_column?(lines)
end
size_from_win_api(verbose: false) click to toggle source
# File lib/tty/screen.rb, line 113
def size_from_win_api(verbose: false)
  require "fiddle" unless defined?(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 75
def width
  size[1]
end
Also aliased as: columns, cols
windows?() click to toggle source
# File lib/tty/screen.rb, line 23
def windows?; true end