tcgetwinsize — get the size of a terminal window
#include <termios.h>
int tcgetwinsize(int fildes, struct winsize *winsize_p);
The tcgetwinsize() function shall get the terminal window size associated with the terminal referred to by fildes and store it in the winsize structure pointed to by winsize_p. The fildes argument is an open file descriptor associated with a terminal. The winsize_p argument is a pointer to a winsize structure.
If the terminal referred to by fildes was opened without O_TTY_INIT and is not a pseudo-terminal, and the terminal window size has not been set by a call to tcsetwinsize(), the terminal window size is unspecified.
If the terminal was opened with O_TTY_INIT or is a pseudo-terminal, and the terminal window size has not been set by a call to tcsetwinsize(), the terminal window size shall be set to an appropriate default (see open).
If the terminal window size has been set by a call to tcsetwinsize(), the values set by that call shall be returned.
Upon successful completion, 0 shall be returned. Otherwise, -1 shall be returned and errno set to indicate the error.
The tcgetwinsize() function shall fail if:
- [EBADF]
- The fildes argument is not a valid file descriptor.
- [ENOTTY]
- The file associated with fildes is not a terminal.
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <termios.h> #include <unistd.h> static volatile sig_atomic_t vrow; static volatile sig_atomic_t vcol; static volatile sig_atomic_t newsize; static void winch_handler(int signum) { struct winsize ws; int sav_errno = errno; (void)signum; // prevent compiler warning that signum is unused // set volatile vars to new winsize, or 0 if unavailable or // too large if (tcgetwinsize(STDERR_FILENO, &ws) == -1) { vrow = vcol = 0; } else { if (ws.ws_row <= SIG_ATOMIC_MAX && ws.ws_col <= SIG_ATOMIC_MAX) { vrow = ws.ws_row; vcol = ws.ws_col; } else { vrow = vcol = 0; } } newsize = 1; errno = sav_errno; } int main(void) { struct sigaction sa; struct winsize ws; sigset_t winch_set; char inbuf[512]; sa.sa_handler = winch_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGWINCH, &sa, NULL); sigemptyset(&winch_set); sigaddset(&winch_set, SIGWINCH); raise(SIGWINCH); // gets the initial winsize for (;;) { if (fgets(inbuf, sizeof inbuf, stdin) == NULL) { if (feof(stdin)) exit(0); else if (errno == EINTR) continue; else { perror("Error reading stdin"); exit(1); } } else { if (newsize) { // prevent updates to volatile vars while we read them sigprocmask(SIG_BLOCK, &winch_set, NULL); ws.ws_row = vrow; ws.ws_col = vcol; sigprocmask(SIG_UNBLOCK, &winch_set, NULL); newsize = 0; } printf("row = %3hu, col = %3hu\n", ws.ws_row, ws.ws_col); // process inbuf ... } } }
Applications should take care to avoid race conditions and other undefined behavior when calling tcgetwinsize() from signal handlers. A common but incorrect idiom is to establish a signal handler for SIGWINCH from which tcgetwinsize() is called to update a global winsize structure. This usage is incorrect as accessing a winsize structure is not guaranteed to be an atomic operation. Instead, applications should have tcgetwinsize() write to a local structure and copy each member the application is interested in to a global variable of type volatile sig_atomic_t. Furthermore, SIGWINCH should be blocked from delivery while the terminal size is read from these global variables to further avoid race conditions. A simpler alternative, if the application is structured in a suitable way, is just to set a flag in the signal handler and then call tcgetwinsize() (and clear the flag) at an appropriate place in the code if the flag has been set.
Multi-threaded applications should avoid the signal handler idiom in general. Instead, it is advised to use sigwait() to wait for the delivery of a SIGWINCH signal.
If the terminal window size changes while a process is in the background, it is not notified via SIGWINCH (which is sent only to the foreground process group). Applications can handle this case by calling tcgetwinsize() if the process receives SIGCONT, to check whether the terminal window size changed while the process was stopped.
If a background process writes to a terminal and the TOSTOP flag is clear (see XBD 11.2.5 Local Modes), the process might not receive SIGTTOU or SIGWINCH signals and thus might not be notified when the terminal window size might have changed. Such processes must periodically poll the current terminal window size if needed.
The tcgetwinsize() function is provided to allow applications to query the current terminal window size. This is necessary for applications intended to be run in terminals whose terminal window size can be changed at runtime. Conventionally, a SIGWINCH signal is delivered to a controlling terminal's foreground process group whenever its terminal window size is changed. By installing a signal handler for SIGWINCH, a process can detect the change to the controlling terminal's window size and take action, e.g. by redrawing its user interface to the new size.
None.
XBD <termios.h>
First released in Issue 8.
return to top of page