tty: arrow keys, backspace, and four terminals nobody was home on
Three things, all in the first minute of using a shell.
**Arrow keys corrupted the two keystrokes after them.** They arrive as single
keycodes -- KEY_UP is 0xE2 -- and were pushed into the input buffer exactly
like that. No terminal works that way, and 0xE2 is the worst possible byte to
have chosen: it is a UTF-8 lead byte for a three-byte character, so a reader
decoding UTF-8 swallowed the next two keys waiting for continuations that never
arrived. consoleintr() sends the DEC/xterm sequences now -- ESC[A, ESC[D,
ESC[5~ and the rest -- which every terminfo entry and every line editor already
knows. Not echoed: in canonical mode echoing them would move the cursor for
real, which was the other half of what "weird glitches" looked like.
**Then the end of the line vanished as you typed into it**, with the buffer
perfectly correct. One line in vt_putc(): backspace erased the cell it moved
onto. That is a line-discipline erase, not the BS control character -- a
program rubbing a character out sends `\b \b`, and the separation matters
because programs also use plain `\b` to go back. zsh does, on every mid-line
edit:
echo alpha \b\b zha \b\b
back two, rewrite the tail, back two again to park the cursor. Against a
destructive backspace that last pair deleted the `ha` it had just drawn.
`\b` moves and nothing else now, 0x7f is ignored as on a real terminal, and
consputc() spells its own erase out as `\b \b` as it already did for serial.
ESC[@, ESC[P and ESC[X went in alongside, because TERM=linux declares them.
**VT3 to VT6 were blank** because nothing ran on them. NVT is 6 and the kernel
made all six; only two had a shell, so Alt+F3 switched to a terminal that was
working perfectly and had nobody to talk to. Four more manifests.
Six shells starting at once is enough concurrency to expose an intermittent
fault in one of them -- one boot in five, a wild jump into .data that runs
until it writes to 0, which nothing catches because SchOS never sets EFER.NXE.
Three boots with two shells, none. Recorded rather than fixed: it is not caused
by the manifests, only made likelier by them.
Also: QEMU_NET forwards host 8080/8081/8000 to guest 80/8080/8000, so the httpd
is reachable from the machine running QEMU instead of being sealed behind NAT.
Verified end to end. And the greeting prints once again -- a replaced
supervisor was announcing a boot that had already happened.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
This commit is contained in:
parent
75ccf9841c
commit
ff7afa746e
9 changed files with 317 additions and 8 deletions
|
|
@ -266,10 +266,16 @@ consputc(int c)
|
|||
if (cons_logonly)
|
||||
return;
|
||||
|
||||
// The kernel's own erase, echoed when you press backspace at a prompt the
|
||||
// line discipline is reading. Now that the terminal's `\b` only moves -- see
|
||||
// the note in vt.c -- this has to spell the erase out, exactly as it already
|
||||
// did for the serial line.
|
||||
if (c == BACKSPACE) {
|
||||
uartputc('\b'); uartputc(' '); uartputc('\b');
|
||||
if (!cons_serial_only)
|
||||
vt_putc(vt_current(), '\b');
|
||||
if (!cons_serial_only) {
|
||||
struct vt *v = vt_current();
|
||||
vt_putc(v, '\b'); vt_putc(v, ' '); vt_putc(v, '\b');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -559,6 +565,51 @@ consoleintr(int (*getc)(void))
|
|||
}
|
||||
}
|
||||
|
||||
// Arrow keys and the rest of the navigation block, as escape sequences.
|
||||
//
|
||||
// These arrive as single keycodes -- KEY_UP is 0xE2 -- and used to be
|
||||
// pushed into the input buffer exactly like that. A terminal has never
|
||||
// worked that way, and 0xE2 in particular is the worst possible choice of
|
||||
// byte: it is a UTF-8 lead byte for a three-byte character, so a reader
|
||||
// decoding UTF-8 swallowed the next two keys waiting for continuations
|
||||
// that never came. Every arrow key corrupted the two keystrokes after it.
|
||||
//
|
||||
// What a terminal sends is `ESC [ A`, and what makes zsh's line editor
|
||||
// work is receiving that. The sequences below are the DEC/xterm ones that
|
||||
// every terminfo entry and every line editor already knows.
|
||||
{
|
||||
const char *esc = 0;
|
||||
switch (c) {
|
||||
case KEY_UP: esc = "\033[A"; break;
|
||||
case KEY_DN: esc = "\033[B"; break;
|
||||
case KEY_RT: esc = "\033[C"; break;
|
||||
case KEY_LF: esc = "\033[D"; break;
|
||||
case KEY_HOME: esc = "\033[H"; break;
|
||||
case KEY_END: esc = "\033[F"; break;
|
||||
case KEY_PGUP: esc = "\033[5~"; break;
|
||||
case KEY_PGDN: esc = "\033[6~"; break;
|
||||
case KEY_INS: esc = "\033[2~"; break;
|
||||
case KEY_DEL: esc = "\033[3~"; break;
|
||||
}
|
||||
if (esc) {
|
||||
// Not echoed. In raw mode -- which is where a line editor lives, and
|
||||
// the only mode where these mean anything -- nothing is echoed anyway.
|
||||
// In canonical mode echoing them would send the escape straight back
|
||||
// to the renderer and move the cursor for real, which is the other
|
||||
// half of what "weird glitches" looked like.
|
||||
for (const char *q = esc; *q; q++) {
|
||||
if (v->in.e - v->in.r >= VT_INPUT_BUF)
|
||||
break; // buffer full: drop the rest
|
||||
v->in.buf[v->in.e++ % VT_INPUT_BUF] = *q;
|
||||
}
|
||||
if (!(v->tio.c_lflag & ICANON)) {
|
||||
v->in.w = v->in.e;
|
||||
wakeup(&v->in.r);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (v->in.e - v->in.r >= VT_INPUT_BUF)
|
||||
continue; // buffer full: drop it
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue