Austin Group Defect Tracker

Aardvark Mark IV


Viewing Issue Simple Details Jump to Notes ] Issue History ] Print ]
ID Category Severity Type Date Submitted Last Update
0001151 [1003.1(2016/18)/Issue7+TC2] System Interfaces Editorial Enhancement Request 2017-06-19 16:41 2020-04-23 13:45
Reporter Clausecker View Status public  
Assigned To
Priority normal Resolution Accepted As Marked  
Status Applied  
Name Robert Clausecker
Organization Fraunhofer Fokus
User Reference
Section termios.h, signal.h, 2.4.3 "Signal Actions", 11.2 "Parameters that Can be Set", new sections tcgetsize and tcsetsize
Page Number 414, 333, 494, 212
Line Number 13877, 11115, 16845–16879, 7040
Interp Status ---
Final Accepted Text See Note: 0003856
Summary 0001151: Introduce new signal SIGWINCH and functions tcsetsize(), tcgetsize() to get/set terminal window size
Description 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]. This
proposal introduces new functions tcgetsize() and tcsetsize() instead of
introducing the ioctl() calls TIOCGWINSZ and TIOCSWINSZ as POSIX
generally does not standardize the ioctl() except for the STREAMS
interface.

The desired action is rather large and posted both inline and as an attachment to the bug report in case the bug tracker garbles it.

[bug 1053]: http://austingroupbugs.net/view.php?id=1053 [^]
Desired Action 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 horizontal or vertical pixels.

    The winsize Structure
    ---------------------

    The <termios.h> 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.
        unsigned short ws_xpixel horizontal size, in pixels.
        unsigned short ws_ypixel vertical size, in pixels.

    In all members named in this volume of POSIX.1 2008, a value of 0
    shall indicate that the value is unknown.

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 entry shall be added immediately after the entry for SIGUSR1.

The section "CHANGE HISTORY" shall be amended appropriately.

Extensions to §2.4.3 Signal Actions
===================================

The list of async-signal-safe functions shall be amended by the
functions tcgetsize() and tcsetsize().

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 <termio.h>

        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

        The following example demonstrates how tcgetsize can be used
        in conjunction with a handler for SIGWINCH to keep track of the
        terminal window size associated with the standard error stream.

        #include <stdio.h>
        #include <signal.h>
        #include <termios.h>
        #include <unistd.h>

        static struct winsize ws;

        static void winch_handler(int signum)
        {

            (void)signum;

            if (tcgetsize(STDERR_FILENO, &ws) == -1)
                /* Handle error. */
        }

        int main()
        {
            struct sigaction sa;

            sa.sa_handler = winch_handler;
            sigemptyset(&sa.sa_mask);
            sa.sa_flags = 0;
            sigaction(SIGWINCH, &sa, NULL);

            for (winch_handler(SIGWINCH); ; pause())
                printf("row = %3d, col = %3d, xpixel = %4d, ypixel = %4d\n",
                    ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
        }

    APPLICATION USAGE

        None.

    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. 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, <termios.h>

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 <termios.h>

        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. If the terminal is a slave pseudo-terminal, a
        SIGWINCH shall also be delivered to the foreground process group
        of the associated master pseudo-terminal. Similarly, if the
        terminal is a master pseudo-terminal, a SIGWINCH shall be
        delivered to the foreground process group of the associated
        slave pseudo-terminal. No signal shall be delivered if the
        terminal size and resolution were changed to the same value they
        had before the tcsetsize() call. If the foreground process
        groups of master and slave pseudo-terminals are the same, only
        one SIGWINCH shall be delivered to the process group. If one of
        the mentioned foreground process groups does not exist, the
        corresponding signal shall not be delivered.

        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 any of the
        terminal attributes defined by this volume of POSIX.1-2008 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, <termios.h>

The "CHANGE HISTORY" section shall be filled in appropriately.

New section §11.2.7 Window Size
===============================

Immediately after §11.2.6 "Special Control Characters," a new section
named "Window Size" shall be added with the following text:

    11.2.7 Window Size

    Routines that need to query or set the terminal window size shall do
    so by using the winsize structure as defined in the <termios.h>
    header.

    Since the winsize structure may include additional members, the
    structure should never be initialized directly by the application as
    this may cause the terminal to behave in a non-conforming manner.
    When opening a terminal device (other than a pseudo-terminal) that
    is not already open in any process, it should be opened with the
    O_TTY_INIT flag before initializing the structure using tcgetsize()
    to ensure that any non-standard elements of the termios structure
    are set to values that result in conforming behavior of the terminal
    interface.

    The members of the winsize structure include (but are not limited
    to):

    +----------------+-------------+----------------------------+
    | Member Type | Member Name | Description |
    +----------------+-------------+----------------------------+
    | unsigned short | ws_row | rows, in characters |
    | unsigned short | ws_col | columns, in characters |
    | unsigned short | ws_xpixel | horizontal size, in pixels |
    | unsigned short | ws_ypixel | vertical size, in pixels |
    +----------------+-------------+----------------------------+

    For each member, a value of 0 indicates that the size is unknown.

    A program can query the window size associated with a terminal using
    the tcgetsize() function and alter the window size using the
    tcsetsize() function. Updating the window size causes a SIGWINCH to
    be delivered to the terminal's foreground process group if any.

    It is implementation-defined whether the window size associated with
    a terminal actually corresponds to the physical size and resolution
    of the physical or virtual terminal device connected to the
    terminal's communication port. If it does correspond, changing the
    size or resolution of the terminal device causes a SIGWINCH to be
    sent to the foreground process group of the terminal.
Tags issue8
Attached Files txt file icon sigwinch-proposal.txt [^] (12,847 bytes) 2017-06-19 16:41
c file icon sigwinch_example.c [^] (2,133 bytes) 2017-06-29 09:12
txt file icon sigwinch-proposal.2.txt [^] (9,590 bytes) 2017-08-31 09:19
c file icon sigwinch_example.2.c [^] (1,750 bytes) 2017-09-14 07:41

- Relationships
related to 0001053Applied 1003.1(2013)/Issue7+TC1 Add a "size" mode to stty(1) 
related to 0001185Applied 1003.1(2016/18)/Issue7+TC2 Additional 3rd option for getting line size. 

-  Notes
(0003786)
shware_systems (reporter)
2017-06-19 17:32
edited on: 2017-08-17 15:24

Extensions to termios.h
=======================

A new subsection named "The winsize_t structure" is added to the header
termios.h right after the section "The termios structure." The type
"unsigned short" follows historical convention. Perhaps the committee
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 horizontal or vertical pixels.

    The winsize_t Structure
    ---------------------

    The <termios.h> header shall define the winsize_t structure, which
    shall include at least the following members:

        unsigned long ws_row rows, in characters.
        unsigned long ws_col columns, in characters.
[XSI] or [UP]
        unsigned long ws_xpixel horizontal size, in pixels, if supported.
        unsigned long ws_ypixel vertical size, in pixels, if supported.
[x]

    In all members named in this volume of POSIX.1 2008, a value of 0
    shall indicate that the value is unknown.
===
Additional edits after as winsize an app reserved identifier, *_t standard / implementation reserved. Use of termios as a type identifier a grandfathered allowance.
Changed to long as display arrays can have now over 65k chars or pixels easily enough with UHD 4k resolutions, yet be logically a single terminal. The future is already here, iow. :-)
Option group added as base POSIX will still only require support for char array or line oriented devices, not pixel or vector based. This was part of Bug 1053 discussion. Moving any added support for vectors or pixels to the base is an Issue 9 matter.
Errors for tcsetsize() is missing ENOSUP as a 'may fail' entry, also discussed, for terminals that have fixed dimensions.
Believe additional text related to LINES and COLUMNS env. variables also desirable.

(0003787)
EdSchouten (reporter)
2017-06-19 18:06

Why call it winsize_t, as opposed to 'struct winsize', which is the current convention?
(0003788)
shware_systems (reporter)
2017-06-19 18:11

Because other applications may use winsize for other purposes, as struct tag or other context.
(0003789)
Clausecker (reporter)
2017-06-19 18:43

struct winsize is the historic name for this structure, used since at least 1989. Renaming it might cause more problems than it solves. Note that tcsetsize and tcgetsize aren't reserved by POSIX either (probably by mistake).

Mandating unsigned long as the type for the members is a bad idea as this contradicts existing implementations. All implementations I checked use unsigned short for this type. As I already mentioned, introducing a typedef windim_t might be a good compromise between compatibility and future expansions.

I don't see how adding ENOTSUP might be a good idea. If the terminal's size cannot be changed, an implementation may either decide to make size changes have no effect (this might be a good idea for serial ports where the device driver cannot change the terminal but e.g. login(8) might want to set up the window size with information from the termcap database) or decide that each other possible terminal size is invalid, returning EINVAL.

Changing the terminal size to itself should never cause an error, even on a fixed-size terminal. Perhaps that invariant should be specified, too.
(0003791)
geoffclare (manager)
2017-06-20 09:22

There are some signal-related problems with the example code.

1. The winch_handler() function needs to save errno on entry and restore it before returning.

2. Having the tcgetsize() call in winch_handler() write directly to the static structure is undefined behaviour. To meet the requirements of the current standard it would have to write to a local structure and then copy each member to a separate static value of type volatile sig_atomic_t. However, since (if accepted) this will go into Issue 8 it may be better to use the new C11 lock-free atomic objects.

3. In main() if a second SIGWINCH arrives during execution of the loop, printf() could end up printing some old and some new values. The code should block SIGWINCH before the loop, and should use sigsuspend() instead of pause().

In addition to correcting the example code, there should be some discussion of points 2 and 3 in the APPLICATION USAGE section, plus a mention that multi-threaded processes should use sigwait() instead of a signal handler.
(0003797)
shware_systems (reporter)
2017-06-22 10:36

Renaming it is required by XSH 2.2.2 Namespaces, afaik, whatever problems ensue. Last it was discussed new identifiers to be standardized were being drawn from the namespace reserved to implementations by the C standard since C89 or with prefixes / suffixes reserved by POSIX and C to implementations, for additions to any of the standard libraries.

That those platforms used a file-scope-visible identifier reserved to applications with winsize is therefore their lookout, not the standard's, and this is not an exception from before Issue 1 or C89 that I see. Whether the interfaces should be posix_get/setsize() or posix_tcget/setsize() is open to debate, perhaps. As proposed what's there is usable if controlled by a visibility macro, to maintain backwards compatibility, but I forgot to add that part to the note.

As apparently no platform is using winsize_t, using longs has no known backwards compatibility considerations for this functionality. Imo bug reports should have been filed over a decade ago with those platforms to obsolete the interfaces where the structure members are shorts, as being short sighted, for collateral issues. I agree use of an opaque type is plausible, but this has the overhead of adding min and max constant specifications too and still obsoletes that structure.

ENOTSUP being returned is a case where the signal would not be generated because no attempt to access the device is being performed. This would be different from a device accepting a set command that has only one valid value, but may have side effects such as doing a form feed or clear screen, so the signal should be generated. This would return EINVAL if another value was passed in, I'd expect.
(0003798)
geoffclare (manager)
2017-06-22 16:45

I see a problem with this part of the tcsetsize() description:
If the terminal is a slave pseudo-terminal, a SIGWINCH shall also be delivered to the foreground process group of the associated master pseudo-terminal. Similarly, if the terminal is a master pseudo-terminal, a SIGWINCH shall be delivered to the foreground process group of the associated slave pseudo-terminal.

Currently the standard does not require the master side to provide a terminal interface nor require that it can have a controlling process or process groups. All it requires of the master is (XBD 3.308) "Anything written on the master device is presented to the slave as an input and anything written on the slave device is presented as an input on the master side."

How best to solve this depends on whether existing applications expect the master side to be a terminal and for what reason (e.g. is it always just in order to receive SIGWINCH when the slave side changes size).
(0003799)
Don Cragun (manager)
2017-06-22 16:52

From the text in this proposal, I don't understand whether a user or application using these interfaces is expected to change the value assigned to ws_xpixel at the same time the value of ws_row is changed (and vice versa) and to change the value assigned to ws_ypixel at the same time the value of ws_col is changed (and vice versa); whether a user or application is only expected to use one pair of these values and never look at or change the other set (and, if so, how to know which pair of values to use to achieve whatever different affects are to be expected from using the two pairs of values); nor whether or not there is any relationship between the ws_row and ws_col values and the ws_[xy]pixel values.

If a user calls tcgetsize() and modifies ws_col and ws_row without changing ws_[xy]pixel before calling tcsetsize() to alter those values, is the system expected to modify the pixel values as a side effect of the call? Should it return an error in this case since the pixel values no longer correlate to the row and col values? Should an application always set the pixel values to 0 if the row/col values are changed and set the row/col values to 0 if the pixel values are changed?

Would the submitter of this bug please add a note with additional text indicating the relationships between these pairs of values, an explanation of what each pair of values is intended to do, and advice for readers of the standard so they know which values to use and which values to modify for the intended use cases of each pair of values?
(0003800)
Clausecker (reporter)
2017-06-22 20:53

> There are some signal-related problems with the example code.

It perhaps might be a good idea to omit the example then as I don't see how to make the example be strictly correct without being overly complicated at the same time.

> Currently the standard does not require the master side to provide a terminal interface nor require that it can have a controlling process or process groups. All it requires of the master is (XBD 3.308) "Anything written on the master device is presented to the slave as an input and anything written on the slave device is presented as an input on the master side."

Reading various kernel source again, this seems to be an exclusive Linux feature. Perhaps this paragraph should be removed or replaced with a paragraph allowing SIGWINCH to be delivered to an implementation defined set of additional processes.

> Would the submitter of this bug please add a note with additional text indicating the relationships between these pairs of values, an explanation of what each pair of values is intended to do, and advice for readers of the standard so they know which values to use and which values to modify for the intended use cases of each pair of values?

This seems to differ between implementation. The general use case for tcsetsize() seems to be for terminal emulators or the kernel's virtual console to set the current terminal size. If I understood the Linux source code correctly, the kernel ignores ws_xpixel and ws_ypixel and treats zeroes as “no change”. Then it tries to find a video mode that has the desired number of rows and columns and changes to that. In Solaris, I have not found any place where a call to tcsetsize() causes a video mode change, though some comments in common/io/ptem.c seem to suggest that it once did. On FreeBSD I have not found such code either, though, changing the video mode does change the terminal window size as reported by ioctl(TIOCGWINSZ, ...); and a SIGWINCH is delivered.

> If a user calls tcgetsize() and modifies ws_col and ws_row without changing ws_[xy]pixel before calling tcsetsize() to alter those values, is the system expected to modify the pixel values as a side effect of the call? Should it return an error in this case since the pixel values no longer correlate to the row and col values? Should an application always set the pixel values to 0 if the row/col values are changed and set the row/col values to 0 if the pixel values are changed?

Note that on real hardware, rows/columns do not determine resolution. It is possible that the console driver supports more than one video mode for the same number of rows and columns, e.g. two modes with different aspect ratios. However, I do not know any implementation that considers ws_[xy]pixel when setting the terminal window size. I wrote that it is implementation defined if tcsetsize() actually changes the terminals resolution (as opposed to merely adjusting the value returned by tcgetsize()) because the behaviour differs among operating systems.
(0003804)
geoffclare (manager)
2017-06-29 09:20

I have created an updated example program with the points from Note: 0003791 fixed, and attached it as sigwinch_example.c

The code above the "cut here" line is for testing purposes, and wouldn't be included in the standard.

It uses sig_atomic_t because that is what's in the current standard and also because C11 lock-free atomic objects are optional in C11, so will likely be optional in Issue 8. We could include a "Note to Reviewers" that says the code should perhaps be updated to use C11 lock-free atomic objects if they are mandated by Issue 8.
(0003818)
geoffclare (manager)
2017-08-17 16:37

An additional change that is needed is to add ws_ as a reserved prefix for <termios.h> in the first table in XSH 2.2.2 (after c_, B[0-9], TC).
(0003820)
Clausecker (reporter)
2017-08-31 09:24

Attached as sigwinch-proposal.2.txt is an updated version of the proposal. The following changes were made:

* As discussed in the telco, ws_xpixel and ws_ypixel were removed from the proposal.
* Some typographic errors were corrected.
* The faulty example code for tcgetsize() was removed.
* An application usage not indicating correct use of tcgetsize() in conjunction with SIGWINCH was added.
* The language about the delivery of SIGWINCH on call of tcsetsize() to pseudo-terminals was removed and replaced by more general language.
* The prefix ws_ was marked as reserved.
* The functions tcsetsize() and tcgetsize() were marked as async-signal-safe.
(0003854)
geoffclare (manager)
2017-09-14 07:42

I have attached an updated version of my example program as sigwinch_example.2.c which omits the pixel fields.
(0003856)
nick (manager)
2017-10-05 16:30
edited on: 2017-10-19 15:18

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 winsize Structure
    ---------------------

    The <termios.h> 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 tcgetwinsize(int, struct winsize *);
    int tcsetwinsize(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: Terminal window size changed.

The section "CHANGE HISTORY" shall be amended appropriately.

On P201 L6734 (XBD 11.1.4) add tcsetwinsize() to the list of functions, after tcsetpgrp().

Change P1410, L46849-46859 in the DESCRIPTION of open() from:
O_TTY_INIT
If path identifies a terminal device other than a pseudo-terminal, the
device is not already open in any process, and either O_TTY_INIT is set in
oflag or O_TTY_INIT has the value zero, open() shall set any non-standard
termios structure terminal parameters to a state that provides conforming
behavior; see XBD Section 11.2 (on page 205). It is unspecified whether
O_TTY_INIT has any effect if the device is already open in any process. If
path identifies the slave side of a pseudo-terminal that is not already open
in any process, open() shall set any non-standard termios structure
terminal parameters to a state that provides conforming behavior,
regardless of whether O_TTY_INIT is set. If path does not identify a
terminal device, O_TTY_INIT shall be ignored.

to:
    
O_TTY_INIT
If path identifies a terminal device other than a pseudo-terminal, the
device is not already open in any process, and either O_TTY_INIT is set in
oflag or O_TTY_INIT has the value zero, open() shall set any non-standard
termios structure terminal parameters to a state that provides conforming
behavior see XBD Section 11.2 (on page 205) and initialize the winsize
structure associated with the terminal to appropriate default settings. It is unspecified whether
O_TTY_INIT has any effect if the device is already open in any process. If
path identifies the slave side of a pseudo-terminal that is not already open
in any process, open() shall set any non-standard termios structure
terminal parameters to a state that provides conforming behavior and initialize the
winsize structure associated with the terminal to appropriate default settings,
regardless of whether O_TTY_INIT is set. If path does not identify a
terminal device, O_TTY_INIT shall be ignored.


New function "tcgetwinsize"
========================

A new page "tcgetwinsize" shall be added to the list of system interfaces.
Its text is analogous to that of tcgetattr and shall be as follows:

    NAME

        tcgetwinsize -- get the size of a terminal window

    SYNOPSIS

        #include <termios.h>

        int tcgetwinsize(int fildes, struct winsize *winsize_p);

    DESCRIPTION

        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 [xref to open()]).
        
        If the terminal window size has been set by a call to tcsetwinsize(), the
        values set by that call shall be returned.

    RETURN VALUE

        Upon succesful completion, 0 shall be returned. Otherwise, -1
        shall be returned and errno set to indicate the error.

    ERRORS

        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.

    EXAMPLES
    
#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 ... */
        }
    }
}



    APPLICATION USAGE

        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
        struct winsize. This usage is incorrect as accessing a struct
        winsize
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 [xref to 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.

    RATIONALE

        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.

    FUTURE DIRECTIONS

        None.

    SEE ALSO

        tcsetwinsize(), <termios.h>

The "CHANGE HISTORY" section shall be filled in appropriately.

New function "tcsetwinsize"
========================

A new page "tcsetwinsize" shall be added to the list of system interfaces.
Its text is analogous to that of tcsetattr and shall be as follows:

    NAME

        tcsetwinsize -- set the size of a terminal window

    SYNOPSIS

        #include <termios.h>

        int tcsetwinsize(int fildes, const struct winsize *winsize_p);

    DESCRIPTION

        The tcsetwinsize() function shall set the terminal 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
        was changed to the same value it had before the tcsetwinsize()
        call. A SIGWINCH may also be delivered to an implementation
        defined set of other processes.

        The tcsetwinsize() function shall return successfully if it was
        able to update all members of the winsize structure
        associated with the terminal.

        It is unspecified whether changing the terminal window
        size causes any changes to the size of the terminal's font.

        The effect of tcsetwinsize() is undefined if the value of the
        winsize structure pointed to by winsize_p was not derived
        from the result of a call to tcgetwinsize() on fildes; an
        application should modify only fields defined by this
        volume of POSIX.1-20xx between the call to tcgetwinsize() and
        tcsetwinsize(), leaving all other fields unmodified.

        No actions defined by this volume of POSIX.1-20xx, other than a
        call to tcsetwinsize(), a close of the last file descriptor in
        the system associated with this terminal device, or an open of
        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
        window size to change.
            
  If tcsetwinsize() is called from a process which is a member of a
  background process group on a fildes associated with its controlling terminal:
  • If the calling thread is blocking SIGTTOU signals or the process is ignoring SIGTTOU signals, the operation completes normally and no signal is sent.

  • Otherwise, a SIGTTOU signal shall be sent to the process group.



    RETURN VALUE

        Upon successful completion, 0 shall be returned. Otherwise, -1
        shall be returned, the terminal window size shall not be changed,
        and errno shall be set to indicate the error.

    ERRORS
    
        The tcsetwinsize() function shall fail if:
        [EBADF]
            The fildes argument is not a valid file descriptor.
        
        [EIO]
            The process group of the writing process is orphaned, the calling thread
             is not blocking SIGTTOU, and the process is not ignoring SIGTTOU.

        [ENOTTY]
            The file associated with fildes is not a terminal.

        The tcsetwinsize() function may fail if:
        [EINVAL]
            An attempt was made to change an attribute represented in
            the winsize structure to an unsupported value.
            
    EXAMPLES

        None.

    APPLICATION USAGE

        If the terminal window of a pseudo terminal is resized, the
        attached master process should invoke tcsetwinsize() to relay the new
        terminal window size to the foreground process group.

        If a process attached to the slave of a pseudo-terminal calls
        tcsetwinsize(), the attached master process should attempt to change the
        screen to reflect the new size.

    RATIONALE

        This standard does not mention the ws_xpixel
        and ws_ypixel fields that appear in the winsize structure of some
        historical implementations. With current hardware, it is not obvious
        that the unsigned short type used for these fields is sufficient and
        no uses of these fields in portable code were found. However,
        since these and other fields may be included in the winsize
        structure, the standard requires that applications use tcgetwinsize()
        to initialize any fields that may be provided by an implementation
        before setting the ws_cols and ws_rows fields using tcsetwinsize()
        to avoid unintentionally destroying data in other fields in this
        structure.

    FUTURE DIRECTIONS

        None.

    SEE ALSO

        tcgetwinsize(), <termios.h>

The "CHANGE HISTORY" section shall be filled in appropriately.

New reserved prefix ws_
=======================

To the first table in §2.2.2, add ws_ as a reserved prefix for the header
<termios.h> (after c_, B[0-9], TC).

tcgetwinsize() and tcsetwinsize() are async-signal-safe
=================================================

To the list of async-signal-safe functions in §2.4.3, add tcgetwinsize()
and tcsetwinsize().

(0003857)
Clausecker (reporter)
2017-10-16 19:28

I'm terribly sorry to reopen this. It turns out (as I didn't knew before) that QNX already specifies tcgetsize with an incompatible signature:

    http://www.qnx.com/developers/docs/6.5.0/index.jsp?topic=%2Fcom.qnx.doc.neutrino_lib_ref%2Ft%2Ftcgetsize.html [^]

It might perhaps be a good idea to change the function names to something like tcgetwinsize() and tcsetwinsize() to avoid this unfortunate name collision before the function has been set into stone.
(0003858)
Clausecker (reporter)
2017-10-16 19:30

See previous comment; QNX already has tcgetsize() and tcsetsize() with an incompatible signature. It might be a good idea to avoid the foreseeable conflict by either adopting the QNX signature or by renaming the POSIX function.
(0003859)
geoffclare (manager)
2017-10-17 14:07

I think it's not just the function signatures that are different in QNX. Their man page for tcsetsize() says it give an EACCES error if "The filedes argument isn't associated with a controlling terminal", whereas we have made the terminal access semantics for tcsetsize() the same as for tcsetattr(), i.e. the SIGTTOU and EIO stuff.

Therefore I would prefer that we rename to tcgetwinsize() and tcsetwinsize().

I think I like these names better anyway, as they go with the winsize structure tag.
(0003860)
Clausecker (reporter)
2017-10-17 18:59

I agree with this proposal. I am again very sorry for bringing this up again, the QNX functions didn't turn up in my initial search for these function names. Another good reason for not adopting the QNX signatures is that they do not easily extend to reporting the terminal resolution in pixels if that is desired by implementations or a future issue of IEEE 1003.1.
(0003862)
geoffclare (manager)
2017-10-19 15:20

Note: 0003856 has been edited to change the function names to tcgetwinsize() and tcsetwinsize().
(0003865)
egmont (reporter)
2017-10-20 20:26
edited on: 2017-10-20 20:37

Allow me please to share a few thoughts/questions regarding ws_xpixel/ws_ypixel. I'm glad these didn't make it to the standard for now; if you ever plan to add them then please get back here and consider these issues.

- Most graphical terminal emulators have some padding (usually 1px) around the character cells. This is I believe both for aesthetical reasons as well better/easier handling of mouse dragging outside the area etc. Rxvt-unicode, pterm (putty) and st (suckless) set ws_[xy]pixel without this value, xterm adds the padding too. (See the matrix at https://bugzilla.gnome.org/show_bug.cgi?id=782576 [^] .) Which one should be the right behavior and why?

- xterm actually does not only add the padding, but also the scrollbar's width to ws_xpixel. Is this correct then?

- How about other graphical UI elements? E.g. what if xterm had a tab bar (like gnome-terminal, konsole etc. have), should it add that too? What if it used CSD (client-side window decoration), would that also count?

- If the answer was "yes" throughout the previous questions, how about tiling (a.k.a. paned) emulators such as Terminator and Tilix?

- If it's only the cell's pixels (not even the padding) then what's the point in storing the entire window's dimensions and worrying about 16-bit overflow, rather than introducing new fields for a single cell's dimensions (as shorts)?

- Continuing the previous question, kitty ( https://github.com/kovidgoyal/kitty [^] ) is the only terminal emulator I've seen so far that allows continuous resize, and splits the "extra" space among some of its character cells, resulting in certain cells being 1px taller or wider than others. Would this be the reason / use case for denoting the entire terminal emulator's dimensions? Wouldn't it cause problems that it's unknown how those extra pixels are distributed?

- What about headless terminal emulators, such as the libvterm library ( https://launchpad.net/libvterm [^] ), or screen or tmux in detached mode?

- What about terminal emulators that can have multiple views, potentially at different pixel sizes? E.g. konsole allows to "split view" and zoom independently, or screen and tmux allow to attach from multiple terminal emulators that potentially have different fonts, or VTE (gnome-terminal and friends) if it ever addresses https://bugzilla.gnome.org/show_bug.cgi?id=103770 [^] .

- What about cool-retro-term ( https://github.com/Swordfish90/cool-retro-term [^] ) where charcells aren't even rectangles?

- What about high DPI screens where some scaling is applied? I'm not familiar with those, but as far as I know, there are at least 2 (pre-scaling and post-scaling), maybe even 3 definitions of "pixel". Which one to use?

Taking one step back and looking at the broader picture: What's the purpose of these variables? What use cases do you have in mind?

Unless these questions are clearly answered, unless a clear intent is seen and clear semantics is defined for these variables, no longer allowing terminal emulators to use these fields inconsistently, there's hardly any point standardizing this feature.

Thanks in advance for considering these questions whenever you get to standardizing ws_[xy]pixels!

(0004829)
geoffclare (manager)
2020-04-23 13:45

When applying this bug I did not follow the instruction:
A new subsection named "The winsize structure" is added to the header termios.h right after the section "The termios structure."
to the letter.

This is because several subsections following "The termios structure" relate to fields in the termios structure. Instead, I added "The winsize Structure" just before the "Attribute Selection" heading.

- Issue History
Date Modified Username Field Change
2017-06-19 16:41 Clausecker New Issue
2017-06-19 16:41 Clausecker File Added: sigwinch-proposal.txt
2017-06-19 16:41 Clausecker Name => Robert Clausecker
2017-06-19 16:41 Clausecker Organization => Fraunhofer Fokus
2017-06-19 16:41 Clausecker Section => termios.h, signal.h, 2.4.3 "Signal Actions", 11.2 "Parameters that Can be Set", new sections tcgetsize and tcsetsize
2017-06-19 16:41 Clausecker Page Number => 414, 333, 494, 212
2017-06-19 16:41 Clausecker Line Number => 13877, 11115, 16845–16879, 7040
2017-06-19 17:32 shware_systems Note Added: 0003786
2017-06-19 17:33 shware_systems Note Edited: 0003786
2017-06-19 18:06 EdSchouten Note Added: 0003787
2017-06-19 18:11 shware_systems Note Added: 0003788
2017-06-19 18:43 Clausecker Note Added: 0003789
2017-06-20 09:22 geoffclare Note Added: 0003791
2017-06-22 10:36 shware_systems Note Added: 0003797
2017-06-22 15:04 geoffclare Relationship added related to 0001053
2017-06-22 15:46 shware_systems Note Edited: 0003786
2017-06-22 16:45 geoffclare Note Added: 0003798
2017-06-22 16:52 Don Cragun Note Added: 0003799
2017-06-22 20:53 Clausecker Note Added: 0003800
2017-06-29 09:12 geoffclare File Added: sigwinch_example.c
2017-06-29 09:20 geoffclare Note Added: 0003804
2017-08-17 15:18 shware_systems Note Edited: 0003786
2017-08-17 15:24 shware_systems Note Edited: 0003786
2017-08-17 16:37 geoffclare Note Added: 0003818
2017-08-31 09:19 Clausecker File Added: sigwinch-proposal.2.txt
2017-08-31 09:24 Clausecker Note Added: 0003820
2017-09-14 07:41 geoffclare File Added: sigwinch_example.2.c
2017-09-14 07:42 geoffclare Note Added: 0003854
2017-10-05 16:30 nick Note Added: 0003856
2017-10-05 16:31 nick Interp Status => ---
2017-10-05 16:31 nick Final Accepted Text => See Note: 0003856
2017-10-05 16:31 nick Status New => Resolution Proposed
2017-10-05 16:31 nick Resolution Open => Accepted As Marked
2017-10-05 16:32 nick Tag Attached: issue8
2017-10-05 16:33 nick Status Resolution Proposed => Resolved
2017-10-16 19:28 Clausecker Note Added: 0003857
2017-10-16 19:30 Clausecker Note Added: 0003858
2017-10-16 19:30 Clausecker Status Resolved => Under Review
2017-10-16 19:30 Clausecker Resolution Accepted As Marked => Reopened
2017-10-17 14:07 geoffclare Note Added: 0003859
2017-10-17 18:59 Clausecker Note Added: 0003860
2017-10-17 18:59 Clausecker Note Added: 0003861
2017-10-17 18:59 Clausecker Note Deleted: 0003861
2017-10-19 15:18 geoffclare Note Edited: 0003856
2017-10-19 15:20 geoffclare Note Added: 0003862
2017-10-19 15:21 geoffclare Status Under Review => Resolved
2017-10-19 15:21 geoffclare Resolution Reopened => Accepted As Marked
2017-10-20 20:16 egmont Note Added: 0003864
2017-10-20 20:26 egmont Note Added: 0003865
2017-10-20 20:26 egmont Note Deleted: 0003864
2017-10-20 20:28 egmont Note Edited: 0003865
2017-10-20 20:31 egmont Note Edited: 0003865
2017-10-20 20:37 egmont Note Edited: 0003865
2019-02-25 16:46 nick Relationship added related to 0001185
2020-04-23 13:45 geoffclare Note Added: 0004829
2020-04-23 13:45 geoffclare Status Resolved => Applied


Mantis 1.1.6[^]
Copyright © 2000 - 2008 Mantis Group
Powered by Mantis Bugtracker