Module: TTY::Screen

Defined in:
lib/tty/screen.rb,
lib/tty/screen/version.rb

Overview

Used for detecting screen properties

Constant Summary collapse

DEFAULT_SIZE =

Default terminal size

[27, 80].freeze
STDOUT_HANDLE =
0xFFFFFFF5
TIOCGWINSZ =

linux

0x5413
TIOCGWINSZ_PPC =

macos, freedbsd, netbsd, openbsd

0x40087468
TIOCGWINSZ_SOL =

solaris

0x5468
VERSION =
"0.8.1"

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.envObject

Holds the environment variables


48
49
50
# File 'lib/tty/screen.rb', line 48

def env
  @env
end

.outputObject

Specifies an output stream


52
53
54
# File 'lib/tty/screen.rb', line 52

def output
  @output
end

Class Method Details

.colsObject


81
82
83
# File 'lib/tty/screen.rb', line 81

def width
  size[1]
end

.columnsObject


80
81
82
# File 'lib/tty/screen.rb', line 80

def width
  size[1]
end

.heightObject


85
86
87
# File 'lib/tty/screen.rb', line 85

def height
  size[0]
end

.ioctl?(control, buf) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Returns:

  • (Boolean)

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


215
216
217
218
219
220
221
# 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

.jruby?Object

.linesObject


91
92
93
# File 'lib/tty/screen.rb', line 91

def height
  size[0]
end

.private_module_function(name) ⇒ Object

Helper to define private functions


16
17
18
19
# File 'lib/tty/screen.rb', line 16

def self.private_module_function(name)
  module_function(name)
  private_class_method(name)
end

.rowsObject


90
91
92
# File 'lib/tty/screen.rb', line 90

def height
  size[0]
end

.size(verbose: false) ⇒ Array[Integer, Integer]

Get terminal rows and columns

Returns:

  • (Array[Integer, Integer])

    return rows and columns


61
62
63
64
65
66
67
68
69
70
71
72
# 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_ansiconObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Detect terminal size from Windows ANSICON


293
294
295
296
297
298
# 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_defaultArray[Integer, Integer]

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Default size for the terminal

Returns:

  • (Array[Integer, Integer])

100
101
102
# File 'lib/tty/screen.rb', line 100

def size_from_default
  DEFAULT_SIZE
end

.size_from_envnil, Array[Integer, Integer]

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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.

Returns:

  • (nil, Array[Integer, Integer])

282
283
284
285
286
287
# 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) ⇒ nil, Array[Integer, Integer]

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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.

Returns:

  • (nil, Array[Integer, Integer])

171
172
173
174
175
176
177
178
179
180
181
182
# 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_ioctlnil, Array[Integer, Integer]

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Read terminal size from Unix ioctl

Returns:

  • (nil, Array[Integer, Integer])

195
196
197
198
199
200
201
202
203
204
205
206
# 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) ⇒ Object


147
148
149
150
151
152
153
154
155
156
# 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) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Detect screen size using Readline


231
232
233
234
235
236
237
238
239
240
241
# 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_sttyObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Detect terminal size from stty utility


261
262
263
264
265
266
267
268
269
# 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_tputObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Detect terminal size from tput utility


247
248
249
250
251
252
253
254
255
# 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) ⇒ Object


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# 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

.widthObject


75
76
77
# File 'lib/tty/screen.rb', line 75

def width
  size[1]
end

.windows?Object

Instance Method Details

#command_exist?(command) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if command exists

Returns:

  • (Boolean)

306
307
308
309
310
311
312
# 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

#nonzero_column?(column) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if number is non zero

return [Boolean]

Returns:

  • (Boolean)

330
331
332
# File 'lib/tty/screen.rb', line 330

def nonzero_column?(column)
  column.to_i > 0
end

#run_command(*args) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Runs command silently capturing the output


318
319
320
321
322
# File 'lib/tty/screen.rb', line 318

def run_command(*args)
  %x(#{args.join(" ")})
rescue IOError, SystemCallError
  nil
end