martial
martial

Reputation: 3883

How to get terminal window width?

I am working on embedded system using C and linux. User can connect to the device via SSH or a console serial cable. He can do this through PuTTY or Tera Term. My question is, after he is connected, how can I know his window's width? I have tried different approaches, they work if I simulate my system on a linux pc, but none of them work on the device:

  1. ioctl()

    struct winsize ws;
    ioctl(..., TIOCGWINSZ, &ws); 
    

    This method works on pc, but it always returns 0 on the device.

  2. tgetnum()

    setupterm((char *)0, 1, (int *)0);
    CGR_INT columns = tgetnum("co");
    

    This method works on pc, but it always returns 80x24 on the device.

  3. getmaxyx()

    CGR_INT xdim;
    CGR_INT ydim;
    initscr();
    refresh();
    getmaxyx(stdscr, ydim, xdim);
    

    This method works on pc, but it always returns 0 on the device

Upvotes: 4

Views: 8471

Answers (4)

Thomas Dickey
Thomas Dickey

Reputation: 54505

A serial device doesn't negotiate the window size, so TIOCGWINSZ will always fail for that case -- unless one loads "correct values", e.g., using the resize program, or uses stty, etc., to do this. But resize is more reliable for that case.

The different results from tgetnum and getmaxxy leave out the information which would help explain this: the value of TERM which is used, and (possibly) the different terminal databases used:

  • termcap (tgetnum) descriptions typically specify the size of the terminal's screen, but that is useful only for hardware terminals. Over the past forty years, those have become less common.
  • terminfo descriptions typically do not bother to specify the size, because the curses initialization will fill in this information, e.g, using TIOCGWINSZ. If it is not available, it will use the size from the terminal description.

Given the results reported by OP, that tgetnum call used a different terminal description than what was used in the curses calls.

Further reading:

Upvotes: 0

LeoNerd
LeoNerd

Reputation: 8532

A serial line does not have a way to negotiate the size of the terminal. Running a real xterm et.al. over a TTY/PTY device pair uses the TIOCSWINSZ/TIOCGWINSZ ioctl() pair to pass this information.

Traditionally, this information was kept in termcap or terminfo about the real physical piece-of-glass-with-phosphor-coating terminals such as a DEC VT220. This is a static fixed database of values known ahead of time, which was correct around the time that a "terminal" literally was this fixed piece of hardware, but doesn't work so well in modern times of terminals being just some piece of program that draws to a framebuffer or similar.

I'd suggest the best method would be to try TIOCGWINSZ, if that fails use terminfo's tgetnum("co"), and if that fails just presume 80x24.

Upvotes: 4

Adrian Cornish
Adrian Cornish

Reputation: 23858

I use this code to print a bunch of equals signs as a divider. Works on most boxes/terms I've tried. Not sure what devices you are talking about. Worth a try I guess ;-)

#include <termios.h>
#include <sys/ioctl.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
   struct winsize ws;
   ioctl(0, TIOCGWINSZ, &ws);

   int i=0;
   for(;i<10*ws.ws_col;++i) printf("=");
   printf("\n");
   return 0;
}

Upvotes: 3

j&#248;rgensen
j&#248;rgensen

Reputation: 10551

TIOCGWINSZ is the way to go. Your device in question has to announce its size, otherwise this just can't work. Take xterm as an example:

device —— ttyS driver —— ttyS0 devnode —— screen/minicom
xterm —— pty devnode —— pty driver —— pts devnode —— bash

xterm does ioctl(TIOCSWINSZ) the first time it is spawned to let the tty driver know the window size. bash and programs spawned from it can then inquire it. If you resize the xterm window, it will tell the tty driver the new size, and xterm also emits SIGWINCH to its child process (bash in this case) to notify it of the size change.

None of this happens with devices (such as yours) that have no idea what they are connected to in the first place. Neither does the tty driver know what is connected to it. (Nor does it usually care.) xterm can tell the driver the size, because it can issue an ioctl, but ioctls are not transported via serial for example. What it would in theory take is a specialized kernel driver that knows how to communicate size changes with your particular device (perhaps a protocol on top of serial so that no rewrite of a core component is needed).

Note that the connected device may not even have a concept of a fixed region—e.g. a printer could be considered to have practically infinitely many lines.

ncurses just assumes 80x24 if it sees 0x0, because the programmer defined it that way. That size may not be correct, and in practice usually is not because people can resize their windows (even if they can't, like on tty1, they can still use stuff like screen(1) and shrink the size to something less than 80x24).

Upvotes: 4

Related Questions