Email or username:

Password:

Forgot your password?
Top-level
Julia Evans

@njvack honestly I didn't dig into that in the post largely because I don't understand it well either, I think it's the "unix terminal driver", but like what is that?? how does it work? it's a weak point for me and I'm hoping to understand it at some point

12 comments
wfk

@b0rk @njvack I think "man termios" may be a good starting point. This documents the ioctl interface of the line discipline of the tty driver, which is what the tty utility uses to do its magic. It is mostly based on the POSIX standard, with some Linux extensions. Key point is that the driver can operate in different modes. Older commands run in cooked mode where the tty driver does very basic command line editing. Bash, pico, vim, etc use raw mode and handle everything themselves.

pelavarre

@b0rk @njvack

⌃W and ⌃U and ⌃Q and ⌃S and so on hide away deep inside: stty all

for example

$ stty -a
...
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;
swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V;
...
$

macOS

% stty -a
...
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;
min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
stop = ^S; susp = ^Z; time = 0; werase = ^W;
%

part of how you can test for this is to try inside of: cat -

the ⌃W and ⌃U will work there, even when Arrow Keys etc don't work there

<=

macOS lets you spell out 'stty -a' as 'stty all', but Linux doesn't

Linux lets you abbreviate 'stty -a' as 'stty --all', but macOS doesn't

'stty -a' reports the 'stty ixon' vs 'stty -ixon' toggle in a different output line apart from these mentions of ^Q ^S, but you often need to add 'stty -ixon' to make the ⌃S forward-search-history properly undo
the ^R reverse-search-history mentioned
by Bash bind -p |grep C-[rs]
or the ^R history-incremental-search-backward
by Zsh bindkey |grep '\^[RS]'

i can't remember how often Zsh needs this workaround = maybe less often than Bash

@b0rk @njvack

⌃W and ⌃U and ⌃Q and ⌃S and so on hide away deep inside: stty all

for example

$ stty -a
...
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;
swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V;
...
$

macOS

% stty -a
...
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;
min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
stop = ^S; susp...

pelavarre

⌃W and ⌃U are not precisely ^W and ^U, because "^" is pure Ascii and more classic, but ⌃ is the Unicode Up Arrowhead U+2303 that's part of ⌃ ⌥ ⇧ ⌘ ← ↑ → ↓ ⎋ ⏎ ⇥ ⇤

Gomijacogeo

@b0rk @njvack It helps to remember that a tty used to just be an RS-232 serial line. A character would come in, an interrupt would get generated, and the tty handler would read the char from a register and put it in a buffer for the attached program to eventually read(). That's raw mode. Cooked is when the driver kept a line-buffer, and would interpret chars as they came over the wire - printable? echo and place in buffer; ^J?, send buffer on to consumer to read(), ^U, kill buffer and start over

Gomijacogeo

@b0rk @njvack ^H? send back '^H ^H' to erase char on terminal, and move the head of the buffer back one. Other chars like ^C, ^Z, ^\, etc would instead send a signal to the process group attached to that tty and the signal handler would be invoked or the process killed.

njvack

@gomijacogeo @b0rk I think the thing I need to keep remembering is that at some point, ... something (I'm not 100% sure what — the tty handler? readline?) is telling _my terminal emulator_ to do something with the characters

Gomijacogeo

@njvack @b0rk Yeah, it was a lot simpler 40+ years ago where each component was a separate, tangible box connected by wires. Made it easy to keep track of what was done where. A modern terminal emulator, on one side talks to the window system and sees keypress events and does advanced font rendering. On the other side, it talks to a pty which is an abstraction of that ancient RS-232 line and is mostly a serialized byte stream that the kernel then treats as input and output.

Gomijacogeo

@njvack @b0rk If the tty is in cooked mode, those simple tasks - echoing, ^H, ^W, etc are done by the tty driver. In raw or cbreak (like raw, but still looks for things like ^C ^Z ^\ etc), then it is the consuming program (eg readline, vim, etc) that reads chars and then decides what to send back to the tty and then to the emulator to be displayed (usually lots of escape codes to handle positioning along with the one or two chars to actually display).

Gomijacogeo

@b0rk @njvack Then X11 came along and we needed a tty-like abstraction to attach these newfangled terminal windows to processes, because all the I/O and job control was wired to talk to a tty-shaped device. And some new side channels were added (e.g. SIGWINCH). So there are a ton of ioctls() to talk to the driver and control/interrogate exactly what kind of tty it really is.

Gomijacogeo

@b0rk @njvack Oh yeah, you also needed ptys for telnet/rlogin/ssh-like services. And the tty driver was already hellishly complex even before GUIs entered the picture. Tons of respect to anyone who unpeels the onion in the modern era - so many moving parts.

Gomijacogeo

@b0rk @njvack One other thing that was an A-ha! moment for me back in the day was learning that stty uses an ioctl() to determine the tty behind stdin, so you can redirect from another tty (assuming you have permission) to see its settings. So, assuming /dev/ttys000 is a different window, I can 'stty -a < /dev/ttys000' and see how the flags change between cat, bash, and vim for example.

Go Up