Terminal control/Dimensions: Difference between revisions
m →{{header|UNIX Shell}}: <lang bash>, not <lang sh>. |
→{{libheader|BSD libc}}: 4.4BSD has TIOCGWINSZ. Older BSD might or might not have it, but this program also uses err() from 4.4BSD. |
||
Line 11: | Line 11: | ||
Almost all terminal devices can do NAWS (Negotiate About Window Size). A terminal emulator like xterm(1) should set the size. A network server like sshd(1) should copy the size from its client. Other devices, such as plain serial ports, might not know the window size. |
Almost all terminal devices can do NAWS (Negotiate About Window Size). A terminal emulator like xterm(1) should set the size. A network server like sshd(1) should copy the size from its client. Other devices, such as plain serial ports, might not know the window size. |
||
{{works with| |
{{works with|BSD|4.4}} |
||
<lang c>#include <sys/ioctl.h> /* ioctl, TIOCGWINSZ */ |
<lang c>#include <sys/ioctl.h> /* ioctl, TIOCGWINSZ */ |
||
#include <err.h> /* err */ |
#include <err.h> /* err */ |
Revision as of 16:18, 1 September 2011
You are encouraged to solve this task according to the task description, using any language you may know.
Determine the height and width of the terminal, and store this information into variables for subsequent use.
C
C provides no standard way to find the size of a terminal.
BSD systems (and some other Unix clones) have TIOCGWINSZ. This ioctl(2) call gets the "window size" of a tty(4) device.
Almost all terminal devices can do NAWS (Negotiate About Window Size). A terminal emulator like xterm(1) should set the size. A network server like sshd(1) should copy the size from its client. Other devices, such as plain serial ports, might not know the window size.
<lang c>#include <sys/ioctl.h> /* ioctl, TIOCGWINSZ */
- include <err.h> /* err */
- include <fcntl.h> /* open */
- include <stdio.h> /* printf */
- include <unistd.h> /* close */
int main() { struct winsize ws; int fd;
/* Open the controlling terminal. */ fd = open("/dev/tty", O_RDWR); if (fd < 0) err(1, "/dev/tty");
/* Get window size of terminal. */ if (ioctl(fd, TIOCGWINSZ, &ws) < 0) err(1, "/dev/tty");
printf("%d rows by %d columns\n", ws.ws_row, ws.ws_col); printf("(%d by %d pixels)\n", ws.ws_xpixel, ws.ws_ypixel);
close(fd); return 0; }</lang>
Windows
Grab a console screen handle, then call GetConsoleScreenBufferInfo()
to get the information. Most consoles have a scroll bar and hold hundreds of lines, but the window shows only 25 or 50 lines. Use the window coordinates to calculate the window size.
<lang c>#include <windows.h>
- include <wchar.h>
int main() { HANDLE console; CONSOLE_SCREEN_BUFFER_INFO info; short rows; short columns; /* Create a handle to the console screen. */ console = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (console == INVALID_HANDLE_VALUE) return 1;
/* Calculate the size of the console window. */ if (GetConsoleScreenBufferInfo(console, &info) == 0) return 1; CloseHandle(console); columns = info.srWindow.Right - info.srWindow.Left + 1; rows = info.srWindow.Bottom - info.srWindow.Top + 1;
wprintf(L"%d columns by %d rows\n", columns, rows);
return 0; }</lang>
Euphoria
<lang Euphoria>include graphics.e
sequence vc integer term_height, term_width
vc = video_config()
term_height = vc[VC_LINES] term_width = vc[VC_COLUMNS]
printf(1,"Terminal height is %d\n",term_height) printf(1,"Terminal width is %d\n",term_width)</lang>
Forth
<lang forth>variable term-width variable term-height
s" gforth" environment? [if]
2drop form ( height width )
[else] \ SwiftForth
get-size ( width height ) swap
[then] term-width ! term-height !</lang>
J
This is not well supported in J, but since the terminal window can be resized at any time and can have its font changed and so on, good design generally dictates that this kind of information be ignored.
Nevertheless, assuming J version 6 in its usual environment, to determine its width and height, in pixels, you can use:
<lang j>_2 {.qsmsize_jijs_</lang>
Note also that this will typically include 37 extra pixels horizontally and 79 extra pixels vertically, which are not available to display text. In other words, if the result was 700 500 you would really have 663 pixels of width and 421 pixels of height.
Locomotive Basic
Locomotive BASIC has no built-in command to get window dimensions, but there is a firmware call to &bb69 (TXT_GET_WINDOW) for this. So we have to use a snippet of Z80 machine code to call the firmware and copy the results from the DE and HL registers to RAM. It looks like this when disassembled:
<lang z80>4000 d5 push de 4001 e5 push hl 4002 cd 69 bb call &bb69 4005 ed 53 20 40 ld (&4020),de 4009 22 22 40 ld (&4022),hl 400c e1 pop hl 400d d1 pop de 400e c9 ret</lang>
This routine gets POKEd into RAM (starting at address &4000) and CALLed from Locomotive BASIC, then the results are retrieved with PEEK:
<lang locobasic>10 s=&4000:SYMBOL AFTER 256:MEMORY s-1 20 FOR i=0 to 14:READ a:POKE s+i,a:NEXT 30 DATA &d5,&e5,&cd,&69,&bb,&ed,&53,&20,&40,&22,&22,&40,&e1,&d1,&c9 40 CALL s 50 h=PEEK(&4020)-PEEK(&4022)+1 60 w=PEEK(&4021)-PEEK(&4023)+1 70 PRINT "window width:"; w; ", height:"; h</lang>
In practice, one would prefer to write the machine code routine as a slightly more elaborate RSX (resident system extension) which is a freely relocatable and therefore more reusable Locomotive BASIC extension. The RSX routine might be called "getwh" and accept pointers to integers, which would simplify the BASIC code to:
<lang locobasic>10 w%=0:h%=0 ' initialize and force integer type 20 |getwh,@w%,@h% ' call RSX and pass variables as pointers 30 PRINT "window width:"; w%; ", height:"; h%</lang>
PicoLisp
<lang PicoLisp>(setq
Width (in '(tput cols) (read)) Height (in '(tput lines) (read)) )</lang>
PureBasic
PureBasic does not have native functions for reading the size of this window, but supports API-functions that allows this.
This code is for Windows only. <lang PureBasic>Macro ConsoleHandle()
GetStdHandle_( #STD_OUTPUT_HANDLE )
EndMacro
Procedure ConsoleWidth()
Protected CBI.CONSOLE_SCREEN_BUFFER_INFO Protected hConsole = ConsoleHandle() GetConsoleScreenBufferInfo_( hConsole, @CBI ) ProcedureReturn CBI\srWindow\right - CBI\srWindow\left + 1
EndProcedure
Procedure ConsoleHeight()
Protected CBI.CONSOLE_SCREEN_BUFFER_INFO Protected hConsole = ConsoleHandle() GetConsoleScreenBufferInfo_( hConsole, @CBI ) ProcedureReturn CBI\srWindow\bottom - CBI\srWindow\top + 1
EndProcedure
If OpenConsole()
x$=Str(ConsoleWidth()) y$=Str(ConsoleHeight()) PrintN("This window is "+x$+"x"+y$+ " chars.") ; Print(#CRLF$+"Press ENTER to exit"):Input()
EndIf</lang>
Python
This uses the ctypes library in order to get the console dimensions on Windows. This code is a slight refactoring of an ActiveState Recipe. For Linux, the tput utility is used.
<lang python>import os
def get_windows_terminal():
from ctypes import windll, create_string_buffer h = windll.kernel32.GetStdHandle(-12) csbi = create_string_buffer(22) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
#return default size if actual size can't be determined if not res: return 80, 25
import struct (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy)\ = struct.unpack("hhhhHhhhhhh", csbi.raw) width = right - left + 1 height = bottom - top + 1
return width, height
def get_linux_terminal():
width = os.popen('tput cols', 'r').readline() height = os.popen('tput lines', 'r').readline()
return int(width), int(height)
print get_linux_terminal() if os.name == 'posix' else get_windows_terminal() </lang>
Retro
This information is provided by Retro in the ch (height) and cw (width) variables. You can manually obtain it using the io ports.
<lang Retro>-3 5 out wait 5 in !cw -4 5 out wait 5 in !ch</lang>
REXX
Some Rexx interpreters don't provide basic terminal control as part of the language.
However, it's possible to determine the size of the terminal window by using external system commands:
<lang rexx>
width = 'tput'( 'cols' )
height = 'tput'( 'lines' )
say 'The terminal is' width 'characters wide'
say 'and has' height 'lines'
</lang>
Older REXX interpretors (such as all the IBM mainframe REXX interpretors, PC/REXX), and R4 support the
LINESIZE built-in function which returns the screen's width.
It's syntax is:
<lang rexx>
width=linesize()
</lang>
Ruby
<lang ruby>def winsize
# Ruby 1.9.3 added 'io/console' to the standard library. require 'io/console' IO.console.winsize
rescue LoadError
# This works with older Ruby, but only with systems # that have a tput(1) command, such as Unix clones. [Integer(`tput li`), Integer(`tput co`)]
end
rows, cols = winsize printf "%d rows by %d columns\n", rows, cols</lang>
Curses.lines
and Curses.cols
return the size of the terminal. The program must call Curses.init_screen
, because without this call, Curses might report 0 lines and 0 columns. Beware that Curses.init_screen
also switches the terminal to screen-oriented mode, and fails on those terminals that cannot support curses.
<lang ruby>require 'curses'
begin
Curses.init_screen
r, c = Curses.lines, Curses.cols
Curses.setpos r / 2, 0 Curses.addstr "#{r} rows by #{c} columns".center(c) Curses.getch
ensure
Curses.close_screen
end</lang>
Seed7
The functions height and width are portable and determine the dimensions of the console window. Height and width are based on terminfo respectively the Windows console API.
<lang seed7>$ include "seed7_05.s7i";
include "console.s7i";
const proc: main is func
local var text: console is STD_NULL; begin console := open(CONSOLE); writeln(console, "height: " <& height(console) lpad 3); writeln(console, "width: " <& width(console) lpad 3); # Terminal windows often restore the previous # content, when a program is terminated. Therefore # the program waits until Return/Enter is pressed. readln; end func;</lang>
Tcl
<lang tcl>set width [exec tput cols] set height [exec tput lines] puts "The terminal is $width characters wide and has $height lines"</lang>
UNIX Shell
<lang bash>#!/bin/sh WIDTH=`tput co` HEIGHT=`tput li` echo "The terminal is $WIDTH characters wide and has $HEIGHT lines."</lang>
If the system has terminfo, then `tput cols`
and `tput rows`
also work. (All recent systems have terminfo, except NetBSD, but NetBSD 6 will have terminfo.) The shorter names co
and li
are the backward-compatible names from termcap.
C Shell
<lang csh>#!/bin/csh -f set WIDTH=`tput co` set HEIGHT=`tput li` echo "The terminal is $WIDTH characters wide and has $HEIGHT lines."</lang>