Deliver the keys that are not characters
An arrow key has never reached this machine. Voyager threw it away for want of anywhere to put it, and a terminal sent ESC [ A, which arrived in the middle of whatever was being read and made it unrecognisable - typing Up at the CosmOS prompt put three bytes in the command line and got "I do not know". So the console names them: one byte each, 0x80 upward, above ASCII so nothing written before them can collide. Up, Down, Left, Right, Home, End and forward Delete, with room above for the paging and function keys. The console normalises, which is what it already does. Behind a window it turns the key somebody pressed into a byte; on a terminal it turns the sequence into the same byte. That is the act it has always performed on Return and Backspace, one layer further along, and it is why a program need not know which of the two it is talking to. What a key MEANS is not the console's business - that belongs to whoever is reading, the same way what is on a disk belongs to the system and what a drive is belongs to the machine. Translated only when standard input really is a terminal. Nothing else sends these sequences, a pipe holds exactly the bytes somebody put in it, and it keeps the Escape-or-Up timing problem out of every test here: a test writes the key values themselves. Line mode drops them, in both front ends, because line mode delivers characters and a line somebody else has finished editing cannot be moved about in. Press.sbx says what it was handed, in hexadecimal and by name, and reads a line before it reads keys so both halves of that rule are checked. Two recordings, one fed as standard input and one as a keyboard, agreeing byte for byte; each break fails exactly one of them. Three checks in terminal.sh type real escape sequences at a pseudo-terminal, which is the only place they are ever read as sequences: that they arrive as keys, that Escape alone is still Escape, and that a character typed straight after an escape is held rather than swallowed. Five recordings re-blessed for Press.sbx appearing on the shared disk, and the whole of that diff is the file's own line and the counts above it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
2a29cebc6b
commit
b3726c950a
@@ -207,6 +207,54 @@
|
||||
// can ask the console to be, it can also ask the console what it currently is.
|
||||
#define CONSOLE_STATUS_CURSOR 0x10
|
||||
|
||||
// ---- Keys that are not characters ----
|
||||
//
|
||||
// An arrow key is not a letter and there is no byte for it, which is why it has never
|
||||
// reached this machine at all: a window threw it away for want of anywhere to put it, and
|
||||
// a terminal sent an escape sequence that arrived in a command line and made it
|
||||
// unrecognisable.
|
||||
//
|
||||
// So the console names them. These are the values it delivers, one byte each, and they are
|
||||
// the console's own: NOT ASCII, and deliberately above it, so nothing that existed before
|
||||
// them can collide. A program reads one the same way it reads a letter.
|
||||
//
|
||||
// THE CONSOLE NORMALISES, WHICH IS WHAT IT ALREADY DOES. Behind a window it turns the key
|
||||
// somebody pressed into a byte; on a terminal it turns the sequence the terminal sent into
|
||||
// the same byte. That is the same act it has always performed on Return and Backspace, one
|
||||
// layer further along, and it is why a program does not have to know which it is talking to.
|
||||
//
|
||||
// WHAT IT DOES NOT DO is decide what they mean. Where the cursor goes, what a line looks
|
||||
// like afterwards and what was typed before are the system's business - see the shell,
|
||||
// which edits its own line - exactly as what is on a disk is the system's business and what
|
||||
// a drive IS belongs to the machine.
|
||||
#define CONSOLE_KEY_UP 0x80
|
||||
#define CONSOLE_KEY_DOWN 0x81
|
||||
#define CONSOLE_KEY_LEFT 0x82
|
||||
#define CONSOLE_KEY_RIGHT 0x83
|
||||
#define CONSOLE_KEY_HOME 0x84
|
||||
#define CONSOLE_KEY_END 0x85
|
||||
// Forward delete, which is the character UNDER the cursor and not the one before it.
|
||||
// Backspace is 0x08 and always has been; these two are different keys that do different
|
||||
// things, and a terminal has always sent different bytes for them.
|
||||
#define CONSOLE_KEY_DELETE 0x86
|
||||
|
||||
// The range, so that anything wanting to know whether a byte is one of these can ask
|
||||
// without naming them all. Room is left above DELETE on purpose: function keys and the
|
||||
// paging keys are the obvious next ones, and adding one should disturb nothing.
|
||||
#define CONSOLE_KEY_FIRST 0x80
|
||||
#define CONSOLE_KEY_LAST 0x8F
|
||||
|
||||
// ---- Only in key mode ----
|
||||
//
|
||||
// LINE MODE DELIVERS CHARACTERS, and these are not characters. A program in line mode is
|
||||
// being handed a line that something else has already finished editing, so a key that means
|
||||
// "move the cursor left" arrived too late to mean anything and putting it in the line would
|
||||
// only corrupt it - which is precisely what an untranslated escape sequence used to do.
|
||||
//
|
||||
// So the console drops them in line mode, wherever it is reading from. That is also what a
|
||||
// real terminal does: canonical mode gives a program backspace and line kill, and has never
|
||||
// given it arrow keys.
|
||||
|
||||
// Puts the terminal back the way it was found. Registered with atexit and called from a
|
||||
// handler for every signal that can end this process and be caught, because a machine that
|
||||
// stops in key mode and does not undo it leaves the shell that started it unusable, which
|
||||
|
||||
Reference in New Issue
Block a user