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
Holds the environment variables @api public
Specifies an output stream @api public
Public Class Methods
# File lib/tty/screen.rb, line 84 def height size[0] end
# File lib/tty/screen.rb, line 30 def jruby?; true end
Helper to define private functions
# File lib/tty/screen.rb, line 15 def self.private_module_function(name) module_function(name) private_class_method(name) end
Get terminal rows and columns
@return [Array[Integer, Integer]]
return rows and columns
@api public
# File lib/tty/screen.rb, line 60 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
Detect terminal size from Windows ANSICON
@api private
# File lib/tty/screen.rb, line 292 def size_from_ansicon return unless @env["ANSICON"] =~ /\((.*)x(.*)\)/ size = [$2, $1].map(&:to_i) size if nonzero_column?(size[1]) end
Default size for the terminal
@return [Array[Integer, Integer]]
@api private
# File lib/tty/screen.rb, line 99 def size_from_default DEFAULT_SIZE end
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 281 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
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 170 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
Read terminal size from Unix ioctl
@return [nil, Array[Integer, Integer]]
@api private
# File lib/tty/screen.rb, line 194 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
# File lib/tty/screen.rb, line 146 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
Detect screen size using Readline
@api private
# File lib/tty/screen.rb, line 230 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
Detect terminal size from stty utility
@api private
# File lib/tty/screen.rb, line 260 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
Detect terminal size from tput utility
@api private
# File lib/tty/screen.rb, line 246 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
# File lib/tty/screen.rb, line 112 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
# File lib/tty/screen.rb, line 74 def width size[1] end
# File lib/tty/screen.rb, line 22 def windows?; true end
Public Instance Methods
Check if command exists
@return [Boolean]
@api private
# File lib/tty/screen.rb, line 305 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
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 214 def ioctl?(control, buf) ($stdout.ioctl(control, buf) >= 0) || ($stdin.ioctl(control, buf) >= 0) || ($stderr.ioctl(control, buf) >= 0) rescue SystemCallError false end
Check if number is non zero
return [Boolean]
@api private
# File lib/tty/screen.rb, line 329 def nonzero_column?(column) column.to_i > 0 end
Runs command silently capturing the output
@api private
# File lib/tty/screen.rb, line 317 def run_command(*args) %x(#{args.join(" ")}) rescue IOError, SystemCallError nil end
Private Instance Methods
# File lib/tty/screen.rb, line 84 def height size[0] end
# File lib/tty/screen.rb, line 30 def jruby?; true end
Get terminal rows and columns
@return [Array[Integer, Integer]]
return rows and columns
@api public
# File lib/tty/screen.rb, line 60 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
Detect terminal size from Windows ANSICON
@api private
# File lib/tty/screen.rb, line 292 def size_from_ansicon return unless @env["ANSICON"] =~ /\((.*)x(.*)\)/ size = [$2, $1].map(&:to_i) size if nonzero_column?(size[1]) end
Default size for the terminal
@return [Array[Integer, Integer]]
@api private
# File lib/tty/screen.rb, line 99 def size_from_default DEFAULT_SIZE end
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 281 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
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 170 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
Read terminal size from Unix ioctl
@return [nil, Array[Integer, Integer]]
@api private
# File lib/tty/screen.rb, line 194 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
# File lib/tty/screen.rb, line 146 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
Detect screen size using Readline
@api private
# File lib/tty/screen.rb, line 230 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
Detect terminal size from stty utility
@api private
# File lib/tty/screen.rb, line 260 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
Detect terminal size from tput utility
@api private
# File lib/tty/screen.rb, line 246 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
# File lib/tty/screen.rb, line 112 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
# File lib/tty/screen.rb, line 74 def width size[1] end
# File lib/tty/screen.rb, line 22 def windows?; true end