Author ====== Robert Clausecker Fraunhofer FOKUS (institute for open communication systems) Introduction ============ This proposal aims to standardize the broadly available signal SIGWINCH and an extension to termios to query and set the terminal window size. This extension is designed to be easily implementable in terms of the TIOCGWINSZ and TIOCSWINSZ ioctl() calls available on many UNIX-like systems. This proposal does not contain an extension to the stty utility as that is already considered in the scope of [bug 1053]. [bug 1053]: http://austingroupbugs.net/view.php?id=1053 Compatibility Impact ==================== This extension defines a new macro SIGWINCH, a new structure winsize and two new functions tcgetsize() and tcsetsize(). The macro SIGWINCH is reserved in §2.2.2 for the header signal.h, the symbols are not reserved. The specification of the signal SIGWINCH and the winsize structure aims to follow historic practice. The specifications of tcgetsize() and tcsetsize() aim to be implementable as trivial wrappers over the common TIOCGWINSZ and TIOCSWINSZ ioctl() calls. It is intended that the specification of these functions is congruent to the historic behaviour of TIOCGWINSZ and TIOCSWINSZ. Extensions to termios.h ======================= A new subsection named "The winsize structure" is added to the header termios.h right after the section "The termios structure." The type "unsigned short" follows historical convention. Perhaps the comittee wishes to instead introduce appropriate integer types for the structure members instead. This might be useful for implementations aiming to support terminals with more than 65535 rows or columns. The winsize Structure --------------------- The header shall define the *winsize* structure, which shall include at least the following members: unsigned short ws_row rows, in characters. unsigned short ws_col columns, in characters. Furthermore, the list of declared functions shall be amended with the following two entries: int tcgetsize(int, struct winsize *); int tcsetsize(int, const struct winsize *); The section "CHANGE HISTORY" shall be amended appropriately. Extensions to signal.h ====================== The table of signals shall be amended with the following entry: Signal: SIGWINCH Default Action: I Description: Window size changed. The section "CHANGE HISTORY" shall be amended appropriately. New function "tcgetsize" ======================== A new page "tcgetsize" shall be added to the list of system interfaces. Its text is analogous to that of tcgetattr and shall be as follows: NAME tcgetsize -- get the window size associated with a terminal SYNOPSIS #include int tcgetsize(int fildes, struct winsize *winsize_p); DESCRIPTION The *tcgetsize()* function shall get the window size associated with the terminal referred to by *fildes* and store them 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. It is implementation defined whether the window size associated with a terminal refers to the actual size and resolution of the terminal connected to the communication port associated with the terminal. The *tcgetsize()* operation is allowed from any process. RETURN VALUE Upon succesful completion, 0 shall be returned. Otherwise, -1 shall be returned and *errno* set to indicate the error. ERRORS The *tcgetsize()* function shall fail if: [EBADF] The *fildes* argument is not a valid file descriptor. [ENOTTY] The file associated with *fildes* is not a terminal. EXAMPLES None. APPLICATION USAGE Applications should take care to avoid race conditions and other undefined behavior when calling *tcgetsize()* from signal handlers. A common idiom is to establish a signal handler for *SIGWINCH* from which *tcgetsize()* is called to update a global struct winsize. This usage is incorrect as writing to a struct winsize is not guaranteed to be an atomic operation. Instead, applications should have *tcgetsize()* 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. Multi-threaded applications should avoid this idiom in general. Instead, it is advised to use *sigwait()* to wait for the delivery of a *SIGWINCH* signal. RATIONALE The *tcgetsize()* function is provided to allow applications to query the current size of a terminal window. This is necessary for applications intended to be run in terminal emulators whose window size can be changed at runtime. Conventionally, a *SIGWINCH* signal is delivered to a terminal's process group whenever its window size is changed. By installing a signal handler for *SIGWINCH*, a process can detect the resizing of its controlling terminal and take action, e.g. by redrawing its user interface to the new size. FUTURE DIRECTIONS None. SEE ALSO tcsetsize, The "CHANGE HISTORY" section shall be filled in appropriately. New function "tcsetsize" ======================== A new page "tcsetsize" shall be added to the list of system interfaces. Its text is analogous to that of tcsetattr and shall be as follows: NAME tcsetsize -- set the window size associated with a terminal SYNOPSIS #include int tcsetsize(int fildes, const struct winsize *winsize_p); DESCRIPTION The *tcsetsize()* function shall set the window size associated with the terminal referred to by the open file descriptor *fildes* (an open file descriptor associated with a terminal) from the *winsize* structure referenced by *winsize_p*. The change shall occur immediately. If the terminal size was changed succesfully, a SIGWINCH shall be delivered to the foreground process group associated with the terminal. No signal shall be delivered if the terminal size and was changed to the same value it had before the *tcsetsize()* call. A SIGWINCH may also be delivered to an implementation defined set of other processes. The *tcsetsize()* function shall return successfully if it was able to alter the terminal size or resolution. It is implementation defined whether changing the window size of a terminal causes any changes in the size or resolution of the terminal emulator's graphical output. The effect of *tcsetsize()* is undefined if the value of the *winsize* structure pointed to by *winsize_p* was not derived from the result of a call to *tcgetsize()* on *fildes*; an application should only modify only fields defined by this volume of POSIX.1-2008 between the call to *tcgetsize()* and *tcsetsize()*, leaving all other fields unmodified. No actions defined by this volume of POSIX.1-2008, other than a call to *tcsetsize()*, a close of the last file descriptor in the system associated with this terminal device, or an open of the first file descriptor in the system associated with this terminal device (using the O_TTY_INIT flag if it is non-zero and the device is not a pseudo-terminal), shall cause the terminal size to change. RETURN VALUE Upon successful completion, 0 shall be returned. Otherwise, -1 shall be returned and *errno* set to indicate the error. ERRORS The *tcsetsize()* shall fail if: [EBADF] The *fildes* argument is not a valid file descriptor. [EINVAL] An attempt was made to change an attribute represented in the *winsize* structure to an unsupported value. [ENOTTY] The file associated with *fildes* is not a terminal. EXAMPLES None. APPLICATION USAGE If the window of a graphical terminal emulator is resized, the terminal emulator should invoke *tcsetsize()* to relay the new window size and resolution to the slave's foreground process group. If a process attached to the slave of a graphical terminal emulator's pseudo-terminal calls *tcsetsize()*, the terminal emulator should attempt to change the window size and resolution to reflect the requested terminal size. RATIONALE None. FUTURE DIRECTIONS None. SEE ALSO tcgetsize, The "CHANGE HISTORY" section shall be filled in appropriately. New reserved prefix ws_ ======================= To the table in §2.2.2, add ws_ as a reserved prefix for the header . tcgetsize() and tcsetsize() are async-signal-safe ================================================= To the list of async-signal-safe functions in §2.4.3, add tcgetsize() and tcsetsize().