The history of Unix's ioctl and signal about window sizes

(utcc.utoronto.ca)

105 points | by ingve 20 hours ago ago

40 comments

  • clausecker 14 hours 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 14 hours 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 13 hours 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 13 hours 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.

          • j4_james 5 hours 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).

          • clausecker 13 hours 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.

    • high_na_euv 13 hours 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 13 hours ago

        I believe this stems from C originally only having 8 significant characters for identifiers.

        • winocm 11 hours ago
        • cesarb 13 hours 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 9 hours ago

            The limited size helps with keeping the code short and simple. ;-)

            • kps 7 hours 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 7 hours 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.

        • layer8 9 hours 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.

        • high_na_euv 12 hours ago

          Majority of those above are above 8 char length.

  • amiga386 10 hours 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/

  • kevin_thibedeau 13 hours 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 9 hours 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 7 hours 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 7 hours ago

          That's what `reset` is so useful for.

    • caf 3 hours ago

      The ioctl works remotely as well, because ssh sends window size messages across the channel.

  • klysm 12 hours 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

    • wpm 7 hours 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.

    • ggm 11 hours 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 9 hours ago

        I believe terminals in the late 70 already could change their geometries. The VT100 could go from 80 to 132 columns.

        • samatman 9 hours ago

          Controlled with DECCOLM, mode ?3. Curious how many terminals besides xterm choose to support that one.

          • kps 6 hours ago

            Most ‘xterm-compatible’ or ‘vt102-compatible’ terminals do, though at least one handles it wrong.

          • rbanffy 5 hours 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.

    • caf 3 hours 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.

  • rbanffy 9 hours 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 8 hours ago

      It's just a driver. Do you feel the same about pipe(4)? As effectively that's mostly what PTYs have become.

      • rbanffy 5 hours ago

        The kernel should never need to concern itself with the nature of what is sending or receiving a stream of bytes.

  • ape4 14 hours ago

    I like this old Unix history stuff

  • ggm 11 hours 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.

  • amelius 11 hours ago

    How do you constrain the window size of a terminal?

  • TOGoS 13 hours 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?

    • thatcks 12 hours 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.)

    • clausecker 13 hours 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.

    • rockorager 9 hours 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.