• clausecker 3 days ago |
    I submitted the proposal for `tcgetwinsize` to POSIX a few years ago. I originally wrote it because I was sick of having to turn off _POSIX_C_SOURCE for just a single file to get glibc to expose TIOCGWINSZ.

    SIGWINCH and the TIOCGWINSZ ioctl() were originally omitted from POSIX as they were considered relevant for windowing systems only and GUI-stuff was considered out of scope for POSIX. Furthermore, POSIX only specifies ioctl() for STREAMS; other interfaces that traditionally use ioctl() calls are specified in POSIX using wrapper functions (which is what the new interface is; it is specified such that you can implement it just by wrapping the traditional TIOCGWINSZ/TIOCSWINSZ ioctl calls).

    My original proposal had functions tcgetsize() and tcsetsize(), but it turns out that QNX already uses these identifiers with an incompatible signature, so a last minute change was made to name these tcgetwinsize() and tcsetwinsize().

    Furthermore, the winsize structure traditionally also has ws_xpixel and ws_ypixel members indicating the resolution of the terminal. This existed primarily becaus on some historical virtual terminals, a client could change the video mode by calling the TIOCSWINSZ ioctl, providing resolution and row/column count for the desired video mode. While no current virtual terminal known to me supports this, the POSIX spec mandates that if a slave PTY calls tcsetwinsize(), the window size update is propagated to the master, allowing terminal emulators to implement this feature if desired. Another objection raised was that the traditional unsigned short type may be insufficient for future high-definition screens and a resolution may not be well defined for some types of terminals like hardcopy or braille terminals.

    I maintain that submitting a feature to POSIX and waiting for a new version of the standard to be released is probably the easiest way to get glibc to implement a feature you want.

    • aumerle 3 days ago |
      Lots of modern terminals support ws_xpixel and ws_ypixel. Hell even the venerable xterm supports it. It is used primarily for displaying raster graphics in modern terminals, see for example: https://sw.kovidgoyal.net/kitty/graphics-protocol/#getting-t...

      And nowadays modern terminals even support in-band resize notification so there is no need for SIGWINCH: https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3...

      • clausecker 3 days ago |
        My point was not that the terminals don't report ws_xpixel and ws_ypixel, but rather that none support the application setting the resolution using tcsetwinsize() with the terminal subsequently changing its video mode / resizing itself to the requested size.
        • aumerle 3 days ago |
          Ah yes, in that case there no terminals that implement it indeed. Though there are a few that implement changing window size via escape code, though in units of cells not pixels. Generally speaking I dont see how applications can use tcsetwinsize() robustly, given that the size in pixels of a cell is determined by font properties and applications runing in the terminal have no knowledge of these, therefore cannot set both the pixel and cell sizes at the same time.
          • clausecker 3 days ago |
            Correct, that was one of the concerns. Note that tcsetwinsize() is mainly provided so that the pty master (i.e. the terminal emulator) can set the window size to be seen by the slave (i.e. the application running in the emulated terminal). The other direction is not explicitly banned though.
          • j4_james 3 days ago |
            FYI, there are a few terminals that can set the window size in pixels (with `CSI 4 t`). And it's also worth mentioning that there were already terminal emulators back in the 1980s that supported in-band resize notifications (lookup `VTEEWR` - Enable Window Event Reports).
    • high_na_euv 3 days ago |
      >tcgetwinsize

      >_POSIX_C_SOURCE

      >TIOCGWINSZ

      >SIGWINCH

      >TIOCSWINSZ

      Jesus christ, this whole fashion among the C and linux people focused on writing shorter, but unreadable names is really terrible habbit

      • wwalexander 3 days ago |
        I believe this stems from C originally only having 8 significant characters for identifiers.
        • cesarb 3 days ago |
          Not only that, but screen space was really limited back then; it was not uncommon to develop on terminals with as low as 80 columns and 24 lines. Having shorter names meant more of the code could fit on the screen at the same time.
          • rbanffy 3 days ago |
            The limited size helps with keeping the code short and simple. ;-)
            • kps 3 days ago |
              Historically there was a bifurcation between scientific/technical computing and business computing. The former wanted to write something close to E = mc², while the latter wanted `MULTIPLY MASS-IN-GRAMS TIMES SPEED-OF-LIGHT-IN-A-VACUUM-IN-METERS-PER-SECOND TIMES SPEED-OF-LIGHT-IN-A-VACUUM-IN-METERS-PER-SECOND GIVING ENERGY-IN-JOULES`. With the dotcom boom, the last vestiges of the old republic were finally swept away, and now even C programmers get slapped for writing `c`.
          • marssaxman 3 days ago |
            I still develop on a terminal with 80 columns, to this day!

            ...but it has 96 rows, and there are five of them, side by side across my monitor. Definitely an improvement! - but I still prefer not to have long rambling Java-style identifiers.

        • high_na_euv 3 days ago |
          Majority of those above are above 8 char length.
        • winocm 3 days ago |
        • layer8 3 days ago |
          That’s only for external identifiers (the one the linker sees), and it’s only six characters. This limit comes from FORTRAN, and in turn comes from the world of 36-bit word mainframes. Those machines didn’t have bytes, only words. Words could represent numbers, or up to 6 characters (in a 6-bit character set, no lowercase letters).

          Internal identifiers and macro names had a lower limit of 31 significant characters in C.

          The more relevant original reason for short identifiers is that code completion wasn’t a thing, and to a lesser extent that screens were at most 80 characters wide.

          • emmelaich 2 days ago |
            The limit for syscalls in early Unix seems to be five though. I forget why.

            Hence, famously, "creat" instead of "create"

      • tyingq 2 days ago |
        I definitely remember, in the 80s, wondering what OS construct might be like a real life "winch". Window change didn't occur to me. Found in a manual later.
  • ape4 3 days ago |
    I like this old Unix history stuff
  • TOGoS 3 days ago |
    Okay, but why is it a signal at all? In the end, isn't it just escape sequences sent back and forth in-band? Wouldn't it be simpler to just let the program read and write those rather than involving a separate system call?
    • clausecker 3 days ago |
      A signal means that the application can immediately redraw its UI to the new dimension even if it is not currently reading input.

      Also, if the size was reported in-band, it would mess up applications that are not aware of that in-band signalling.

    • thatcks 3 days ago |
      My understanding is that the original SunOS windowing system was deeply connected to the kernel (for instance, windows were devices). The original purpose of the signal (in SunOS) appears to have been telling graphical programs that their window had 'changed' and needed to be repainted (I don't know if this was simply from resizing or if it was any damage), which makes a certain amount of sense for a kernel based windowing system (the kernel is right there and you need some way to notify programs). The use of the signal for telling programs in terminal windows that the window had resized appears to have been distinctly secondary, and only the latter can interact with their window through in-band escape sequences.

      (I'm the author of the linked-to article.)

    • rockorager 3 days ago |
      Exactly - these should be reported along with any other input event as an escape sequence. There are other comments on this post that link to how this is done.
    • gomijacogeo 2 days ago |
      As with everything tty related, there's a lot of history baked into the architecture. As new features arrive, you have to find some place in that architecture to insert it, perhaps awkwardly, but it beats rewriting the world.

      So we start the interactive age with teletypes, keyboards and paper. Nothing beyond printing the characters, ringing the bell, and the basic ACSII chars for moving right (SPACE, TAB), left (BS, CR), and down (VT, FF, LF). The main editor is a line editor called ed.

      Move ahead a few years and you have 'glass' ttys, every one of them different. This is the termcap and curses era. The screen size is baked into the termcap entry. Editors are now interactive 'screen' editors, vi and emacs mostly. Pretty soon, some terminals can support multiple font sizes and the LINES and COLUMNS environment variables can override the termcap entries. Terminals are still separate devices connected via RS-232 and all communication is in-band.

      Move ahead a few more years and we're now in the era of workstations with bitmapped graphics (and their Mac and PC equivalents). 'Terminals' are now interactive graphics programs that run under the window system (either X11 or proprietary), so they need to translate kbd and mouse events into the ASCII byte stream onto a pty to emulate the old RS-232 convention to talk to the "line dscipline" part of the kernel's tty driver. But now, changing the size of a window is trivial; people do it all the time. They do it in the middle of vi sessions. They do it when they've ^Z'd out of vi and then expect vi to know the new size when they fg back into it. You can't just blast escape codes onto the line as whatever is reading stdin is likely not equipped to deal with it. At the very least, the in-band exchange has to be initiated by the 'computer' side of the pty. So, they added SIGWINCH and it was an out-of-band signal sent from the terminal emulator program through the pty (which, again, is a software abstraction in the os as opposed to an RS-232 line) to tell the other side of the pty that, when it has a chance, it should re-query the size of the terminal. It took a little while to nail down the semantics of the shell notifying backgrounded processes, but it got the job done. But it likely doesn't make a lick of sense to anyone that doesn't remember the good ol' days.

      • TOGoS a day ago |
        Thanks. The case about background processes makes it click. I would expect communication between the terminal app and the shell on the other end to still use in-band signalling, since it might be over RS-232 or SSH or anything else, but then that process that's currently playing mainframe would want to propagate that information to everything 'behind' it, and a signal makes sense for that.
  • kevin_thibedeau 3 days ago |
    You don't have to use the ioctl. The xterm escape code to query window size is widely supported across terminal emulators. It has the distinct advantage of working remotely and from non-unix systems.
    • rockorager 3 days ago |
      The single downside being you can get signals for SIGWINCH, while the (legacy) xterm escape sequence requires polling.

      I wrote a proposal[0] to have terminals send these updates in-band, instead of requiring polling a-la xterm. So far, foot, ghostty, iterm2, and kitty have implemented this feature on the terminal side. Neovim, notably, also supports this on the client side.

      [0]: https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3...

      • clausecker 3 days ago |
        This stuff sucks because if the application forgets to turn it back off, the terminal pukes random control characters into your shell every time the window is resized.
        • mrlonglong 3 days ago |
          That's what `reset` is so useful for.
    • caf 3 days ago |
      The ioctl works remotely as well, because ssh sends window size messages across the channel.
      • kevin_thibedeau 3 days ago |
        Terminals still run over raw serial ports.
  • klysm 3 days ago |
    Terminals, ttys, etc. have some of the most arcane and outdated APIs I am aware of. It's like a window back into the 1970s
    • ggm 3 days ago |
      The window part is 1980s. the window back is deeper than the 70s, because the termcap/terminfo specs go into Teletype ASR33 territory which is 70s implementations of a core technology from the Baudot code era.
      • rbanffy 3 days ago |
        I believe terminals in the late 70 already could change their geometries. The VT100 could go from 80 to 132 columns.
        • samatman 3 days ago |
          Controlled with DECCOLM, mode ?3. Curious how many terminals besides xterm choose to support that one.
          • kps 3 days ago |
            Most ‘xterm-compatible’ or ‘vt102-compatible’ terminals do, though at least one handles it wrong.
          • rbanffy 3 days ago |
            I have an IBM 3151 (the late model, made by ADDS - pretty much all late-gen terminals are ADDS) that does.

            Reminds me I need to bring it here.

    • wpm 3 days ago |
      It's one of the reasons I love them. A ball of insane, esoteric, baffling, confusing cruft, layered in a century long onion, with connections to the very basics of telecommunications. At any moment, both the simplest interface and the most complex, both the beautiful speckled black void of the night sky, and the horrific gaping maw of a Lovecraftian beast.
    • caf 3 days ago |
      The nomenclature "hanging up" a terminal even dates back to pulse-dialling landline phones where the receiver was physically hung up on a hook to activate a switch that opened the circuit and ended the call.
  • amelius 3 days ago |
    How do you constrain the window size of a terminal?
  • ggm 3 days ago |
    Signal handling demands constraints in how much you do. I never enjoyed coding to unix signals, I always wound up finding a new way to trample on that state you assume is clean, but inside the sig handler it turns out things are not what you think sometimes. The manual page used to be pretty explicit: do as little as humanly possible inside this execution context.

    I am probably mis-remembering but my memory is getting a signal while handling another signal was a point of pain.

  • amiga386 3 days ago |
    For anybody wondering "why couldn't they just...", there is a lot of complexity in TTYs and PTYs. The kernel is even involved in line discipline and job control. Those Ctrl+Z or Ctrl+C keys you press? The kernel gets involved.

    It may seem strange that there's a process signal for window-size changes, but that's what was needed back in 1985. There's a similar dance with the Amiga's console.device, but both Unix and Amiga TTYs are better than Windows' terminals where there was no such thing as a PTY until Windows 10, you had to make direct API calls, and even now it's still pretty slow: https://devblogs.microsoft.com/commandline/windows-command-l...

    This page is a great overview pf the Unix TTY/PTY subsystem: https://www.linusakesson.net/programming/tty/

  • rbanffy 3 days ago |
    I have conflicting feelings abou This. The OS should not be concerned by this kind of thing. This is completely in the realm of the shell and many physical could change the number of rows or columns either on user command via its settings or through the host sending an equivalent command.

    This does not belong into a kernel.

    • akira2501 3 days ago |
      It's just a driver. Do you feel the same about pipe(4)? As effectively that's mostly what PTYs have become.
      • rbanffy 3 days ago |
        The kernel should never need to concern itself with the nature of what is sending or receiving a stream of bytes.
    • emmelaich 2 days ago |
      You're not alone, and this is why plan9 has only 51 syscalls instead of the hundreds for Linux/BSD/... and thousands for Windows.