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
Specifies an output stream
@api public
Public Class Methods
# File lib/tty/screen.rb, line 63 def height size[0] end
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
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
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
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
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
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
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
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
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
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
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
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
Public Instance Methods
# File lib/tty/screen.rb, line 268 def jruby? RbConfig::CONFIG['ruby_install_name'] == 'jruby' end
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
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
# File lib/tty/screen.rb, line 63 def height size[0] end
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
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
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
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
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
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
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
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
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
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
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