Determining Terminal Size

Problem

Within a terminal-based application, you want to find the size of the terminal: how many rows and columns are available for you to draw on.

Solution

This is easy if you e using the Curses library. This example uses the Curses.program wrapper described in Recipe 21.5:

	Curses.program do |scr|
	 max_y, max_x = scr.maxy, scr.maxx

	 scr.setpos(0, 0)
	 scr.addstr("Your terminal size is #{max_x}x#{max_y}. Press any key to exit.")
	 scr.getch
	end

Its a little less easy with Ncurses: you have to pass in two arrays to the underlying C libraries, and extract the numbers from the arrays. Again, this example uses the Ncurses wrapper from Recipe 21.5:

	Ncurses.program do |scr|
	 max_y, max_x = [], []
	 scr.getmaxyx(max_y, max_x)
	 max_y, max_x = max_y[0], max_x[0]

	 str = "Your 
terminal size is #{max_x}x#{max_y}. Press any key to exit."
	 scr.mvaddstr(0, 0, str)
	 scr.getch
	end

If you e not using a Curses-style library, its not easy at all.

Discussion

If you plan to simulate graphical elements on a textual terminal, subdivide it into virtual windows, or print justified output, youll need to know the terminals dimensions. For decades, the standard terminal size has been 25 rows by 80 columns, but modern GUIs and high screen resolutions let users create text terminals of almost any size. Its okay to enforce a minimum terminal size, but its a bad idea to assume that the terminal is any specific size.

The terminal size is a very useful piece of information to have, but its not an easy one to get. The Curses library was written to solve this kind of problem, but if you e willing to go into the operating system API, or if you e on Windows where Curses is not a standard feature, you can find the terminal size without letting a Curses-style library take over your whole application.

On Unix systems (including Mac OS X), you can make an ioctl system call to get the terminal size. Since you e calling out to the underlying operating system, youll need to use strange constants and C-like structures to carry the response:

	TIOCGWINSZ = 0x5413 # For an Intel processor
	# TIOCGWINSZ = 0x40087468 # For a PowerPC processor

	def terminal_size
	 rows, cols = 25, 80
	 buf = [ 0, 0, 0, 0 ].pack("SSSS")
	 if STDOUT.ioctl(TIOCGWINSZ, buf) >= 0 then
	 rows, cols, row_pixels, col_pixels = buf.unpack("SSSS")[0..1]
	 end
	 return rows, cols
	end

	terminal_size # => [21, 80]

Here, the methods pack and unpack convert between a four-element array and a string that is modified in-place by the ioctl call. After the call, the first two elements of the array contain the number of rows and columns for the terminal. Note that the first argument to ioctl is architecture-dependent.

The Windows version works the same way, although you must jump through more hoops and the system call returns a much bigger data structure:

	STDOUT_HANDLE = 0xFFFFFFF5
	def terminal_size
	 m_GetStdHandle = Win32API.new(kernel32, GetStdHandle, [L], L)
	 m_GetConsoleScreenBufferInfo = Win32API.new (kernel32,
	 GetConsoleScreenBufferInfo,
	 [L, P], L )
	 format = SSSSSssssSS
	 buf = ([0] * format.size).pack(format)
	 stdout_handle = m_GetStdHandle.call(STDOUT_HANDLE)

	 m_GetConsoleScreenBufferInfo.call(stdout_handle, buf)
	 (bufx, bufy, curx, cury, wattr,
	 left, top, right, bottom, maxx, maxy) = buf.unpack(format)
	 return bottom - top + 1, right - left + 1
	end

	 
terminal_size # => [25, 80]

If all else fails, on Unix systems you can call out to the stty command:

	def terminal_size
	 %x{stty size}.split.collect { |x| x.to_i }
	end

	terminal_size 	 # => [21, 80]

See Also

  • The ioctl code is based on code posted to ruby-talk by Paul Brannan (http://blade.nagaokaut.ac.jp/cgi-bin/rcat.rb/ruby/ruby-talk/40350)
  • The Windows code is based on code in the Win32API_Console library, a simple Ruby wrapper around Windows console-related API calls (http://rb-w32mod.sourceforge.net/)
  • Recipe 21.5, "Setting Up and Tearing Down a Curses Program"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net