How does less know the terminal resolution?

If you're looking for a way to check from a script, you can do either of these:

  • Run tput cols and tput lines, as manatwork suggests
  • check the values of $LINES and $COLUMNS

But if you want the details, here we go:

For virtual terminals (xterm, et al) there is an ioctl() system call that will tell you what size the window is. If it can, less uses this call. Furthermore, when you change the size of the window, whatever's running in that window receives a SIGWINCH signal that lets less know that it should check for a new window size. For instance, I started a less running (as process ID 16663), connected to it with strace, and resized the window. This is what I saw:

$ strace -p 16663
Process 16663 attached - interrupt to quit
read(3, 0xbfb1f10f, 1)                  = ? ERESTARTSYS (To be restarted)
--- SIGWINCH (Window changed) @ 0 (0) ---
rt_sigaction(SIGWINCH, {0x805cf10, [WINCH], SA_RESTART}, {0x805cf10, [WINCH], SA_RESTART}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig -icanon -echo ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig -icanon -echo ...}) = 0
ioctl(1, TIOCGWINSZ, {ws_row=40, ws_col=80, ws_xpixel=0, ws_ypixel=0}) = 0
ioctl(2, TIOCGWINSZ, {ws_row=40, ws_col=80, ws_xpixel=0, ws_ypixel=0}) = 0

This is also what tput cols and tput lines do behind the scenes, if they can. For more info on this method, see man tty-ioctl and search for TIOCGWINSZ.

For other terminals such as those connected to serial ports, though, there's no way to get this info directly. In that case, less starts looking for clues in the environment variables.

  • LINES and COLUMNS will often be set to the terminal dimensions. In fact, if bash or zsh can find the terminal dimensions, it will automatically set these variables itself, to make it easy for not-so-clever programs to see the terminal size. However, most other shells, including dash and tcsh, do not set these variables.
  • TERM is usually set to the terminal type, in which case the terminfo database may contain the expected size of the terminal. If tput rows cannot use the IOCTL (for instance, if you're connected over a serial port), it will fall back to the values recorded here. For a terminal whose size can change, this is only a guess and is likely to be wrong.

For more info, see man tput for the command to control the terminal, and man terminfo for a list of things you can tell the terminal to do.


If you take a look at the source code, you will know less calls ioctl() to retrieve the window size on Linux.

#ifdef TIOCGWINSZ
    {
        struct winsize w;
        if (ioctl(2, TIOCGWINSZ, &w) == 0)
        {
            if (w.ws_row > 0)
                sys_height = w.ws_row;
            if (w.ws_col > 0)
                sys_width = w.ws_col;
        }
    }
#else
#ifdef WIOCGETD
    {
        struct uwdata w;
        if (ioctl(2, WIOCGETD, &w) == 0)
        {
            if (w.uw_height > 0)
                sys_height = w.uw_height / w.uw_vs;
            if (w.uw_width > 0)
                sys_width = w.uw_width / w.uw_hs;
        }
    }
#endif

Tags:

Terminal

Less