tty: Alt+1..6 switches terminals, and AltGr is left alone
Alt+F1..F6 was wrong in three separate ways, and the third is the one that
matters most here.
Alt+F4 the host window manager closes the QEMU window
Alt+F2 GNOME and KDE open a run dialog
Ctrl+Alt+F1..F6 the host switches its *own* virtual terminals
AltGr+anything Polish diacritics -- a c e l n o s x z with their marks --
and on some setups AltGr is delivered as Ctrl+Alt
Ctrl+Shift+key widely bound inside applications
Scroll Lock not present on many keyboards
Alt+1..6 survives all of it: the number row, so no function key is involved,
and left Alt, so AltGr is untouched.
Right Alt is tracked as its own modifier bit now. It used to share ALT, which
is harmless for typing -- neither is used to look up a character -- and wrong
for chords, because it made AltGr indistinguishable from Alt.
And the test is an equality rather than a mask:
if ((shift & (CTL | SHIFT | ALT | RALT)) == ALT && ...)
`shift & ALT` fired on Ctrl+Alt+1 and AltGr+1 as well, so a chord aimed at the
host -- or an ordinary Polish keystroke -- would have switched terminals
behind your back. That was a real bug in what shipped yesterday.
Verified with QEMU's sendkey, comparing screendump hashes:
start (tty1) 86cee2284878
Ctrl+Alt+2 86cee2284878 identical -- did not fire
AltGr+2 86cee2284878 identical -- did not fire
Alt+2 114562804a78 switched
Alt+1 86cee2284878 back, pixel-identical to the start
schfs-check passes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
This commit is contained in:
parent
a5b0d5a0ef
commit
91a6d440d6
6 changed files with 71 additions and 12 deletions
|
|
@ -18,7 +18,7 @@ its distribution.
|
||||||
| API | **SchAPI** | The native programming interface — *designed, not built* |
|
| API | **SchAPI** | The native programming interface — *designed, not built* |
|
||||||
| Compositor | **Schcom** | Owns the display; v0 runs |
|
| Compositor | **Schcom** | Owns the display; v0 runs |
|
||||||
|
|
||||||
Six virtual terminals sit under all of it -- `/dev/tty1`..`tty6`, Alt+F1..F6 --
|
Six virtual terminals sit under all of it -- `/dev/tty1`..`tty6`, Alt+1..6 --
|
||||||
each with its own screen, line discipline and foreground process group. See
|
each with its own screen, line discipline and foreground process group. See
|
||||||
[terminals.md](terminals.md).
|
[terminals.md](terminals.md).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
||||||
# Virtual terminals
|
# Virtual terminals
|
||||||
|
|
||||||
Six of them, `/dev/tty1` through `/dev/tty6`, reached with **Alt+F1..Alt+F6**
|
Six of them, `/dev/tty1` through `/dev/tty6`, reached with **Alt+1 .. Alt+6**
|
||||||
or with `chvt N`. Each one is an independent terminal: its own screen, its own
|
or with `chvt N`. Each one is an independent terminal: its own screen, its own
|
||||||
cursor, its own line discipline, its own foreground process group and its own
|
cursor, its own line discipline, its own foreground process group and its own
|
||||||
session. Two shells on two terminals are two unrelated processes that happen to
|
session. Two shells on two terminals are two unrelated processes that happen to
|
||||||
share a keyboard and a screen.
|
share a keyboard and a screen.
|
||||||
|
|
||||||
Alt+F7 is deliberately unused. That is where a display server goes.
|
A seventh is deliberately not bound. That is where a display server goes.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ Linux's numbers, because they do Linux's job.
|
||||||
|
|
||||||
## The keyboard
|
## The keyboard
|
||||||
|
|
||||||
`kbd.c` recognises Alt+F1..F6 and returns `KEY_ALT_F1 + n`, which is above
|
`kbd.c` recognises Alt+1..6 and returns `KEY_ALT_F1 + n`, which is above
|
||||||
0xFF on purpose: every value that fits in a byte is already either a character
|
0xFF on purpose: every value that fits in a byte is already either a character
|
||||||
or a UTF-8 continuation byte, and a chord that collided with one would be typed
|
or a UTF-8 continuation byte, and a chord that collided with one would be typed
|
||||||
into the terminal instead of switching away from it.
|
into the terminal instead of switching away from it.
|
||||||
|
|
@ -106,6 +106,33 @@ into the terminal instead of switching away from it.
|
||||||
What the chord *means* is decided in `console.c`, where the terminals are. The
|
What the chord *means* is decided in `console.c`, where the terminals are. The
|
||||||
keyboard driver names it and stops.
|
keyboard driver names it and stops.
|
||||||
|
|
||||||
|
### Why the number row, and why left Alt
|
||||||
|
|
||||||
|
Everything else was already spoken for:
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| `Alt+F4` | the host window manager closes the QEMU window |
|
||||||
|
| `Alt+F2` | GNOME and KDE open a run dialog |
|
||||||
|
| `Ctrl+Alt+F1..F6` | the host switches its **own** virtual terminals |
|
||||||
|
| `AltGr+`anything | Polish diacritics — ą ć ę ł ń ó ś ź ż — and on some setups AltGr *is* Ctrl+Alt |
|
||||||
|
| `Ctrl+Shift+`key | widely bound inside applications |
|
||||||
|
| Scroll Lock | not present on many keyboards |
|
||||||
|
|
||||||
|
`Alt+1..6` survives all of it.
|
||||||
|
|
||||||
|
The modifier test is an **equality**, not a mask:
|
||||||
|
|
||||||
|
```c
|
||||||
|
if ((shift & (CTL | SHIFT | ALT | RALT)) == ALT && ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
`shift & ALT` would fire on `Ctrl+Alt+1` and on `AltGr+1` too, so a chord aimed
|
||||||
|
at the host — or an ordinary Polish keystroke — would switch terminals behind
|
||||||
|
your back. Right Alt is tracked as its own bit (`RALT`) for exactly this
|
||||||
|
reason; it used to share `ALT`, which is harmless for typing, because neither
|
||||||
|
is used to look up a character, and wrong for chords.
|
||||||
|
|
||||||
Input goes to whichever terminal is on screen. That is what makes a terminal a
|
Input goes to whichever terminal is on screen. That is what makes a terminal a
|
||||||
terminal: the keyboard follows the eyes.
|
terminal: the keyboard follows the eyes.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,14 @@
|
||||||
|
|
||||||
#define E0ESC (1<<6)
|
#define E0ESC (1<<6)
|
||||||
|
|
||||||
|
// Right Alt, tracked apart from left Alt.
|
||||||
|
//
|
||||||
|
// They used to be the same bit, which is fine for typing -- neither is used to
|
||||||
|
// look up a character -- and wrong for chords. On a Polish layout AltGr is how
|
||||||
|
// you type a c e l n o s x z with their diacritics, and it is held constantly.
|
||||||
|
// Nothing the kernel binds may involve it.
|
||||||
|
#define RALT (1<<7)
|
||||||
|
|
||||||
// Special keycodes
|
// Special keycodes
|
||||||
#define KEY_HOME 0xE0
|
#define KEY_HOME 0xE0
|
||||||
#define KEY_END 0xE1
|
#define KEY_END 0xE1
|
||||||
|
|
@ -28,14 +36,32 @@
|
||||||
#define KEY_INS 0xE8
|
#define KEY_INS 0xE8
|
||||||
#define KEY_DEL 0xE9
|
#define KEY_DEL 0xE9
|
||||||
|
|
||||||
// Alt+F1..Alt+F6 -- switch virtual terminal.
|
// Alt+1..Alt+6 -- switch virtual terminal.
|
||||||
//
|
//
|
||||||
// Above 0xFF deliberately. kbdgetc returns an int and consoleintr takes one,
|
// Above 0xFF deliberately. kbdgetc returns an int and consoleintr takes one,
|
||||||
// and every value that fits in a byte is already either a character or a
|
// and every value that fits in a byte is already either a character or a
|
||||||
// UTF-8 continuation byte. A chord that collided with one of those would be
|
// UTF-8 continuation byte. A chord that collided with one of those would be
|
||||||
// typed into the terminal instead of switching away from it.
|
// typed into the terminal instead of switching away from it.
|
||||||
|
// Alt+1..Alt+6 -- switch virtual terminal.
|
||||||
|
//
|
||||||
|
// Above 0xFF deliberately. kbdgetc returns an int and consoleintr takes one,
|
||||||
|
// and every value that fits in a byte is already either a character or a
|
||||||
|
// UTF-8 continuation byte. A chord that collided with one would be typed into
|
||||||
|
// the terminal instead of switching away from it.
|
||||||
|
//
|
||||||
|
// The number row rather than the function keys, and left Alt rather than
|
||||||
|
// either, because everything else was already taken:
|
||||||
|
//
|
||||||
|
// Alt+F4 the host window manager closes the QEMU window
|
||||||
|
// Alt+F2 GNOME and KDE open a run dialog
|
||||||
|
// Ctrl+Alt+F1..F6 the host switches its *own* virtual terminals
|
||||||
|
// AltGr+anything Polish diacritics, and AltGr is Ctrl+Alt on some setups
|
||||||
|
// Ctrl+Shift+key widely bound inside applications
|
||||||
|
//
|
||||||
|
// Alt+digit survives all of that. It is matched exactly -- Ctrl+Alt+1 and
|
||||||
|
// AltGr+1 do nothing -- so a chord meant for the host never reaches here.
|
||||||
#define KEY_ALT_F1 0x120
|
#define KEY_ALT_F1 0x120
|
||||||
#define SCAN_F1 0x3B // F1..F6 are 0x3B..0x40
|
#define SCAN_1 0x02 // 1..6 are scancodes 0x02..0x07
|
||||||
|
|
||||||
// C('A') == Control-A
|
// C('A') == Control-A
|
||||||
#define C(x) (x - '@')
|
#define C(x) (x - '@')
|
||||||
|
|
@ -47,7 +73,7 @@ static uchar shiftcode[256] =
|
||||||
[0x36] SHIFT,
|
[0x36] SHIFT,
|
||||||
[0x38] ALT,
|
[0x38] ALT,
|
||||||
[0x9D] CTL,
|
[0x9D] CTL,
|
||||||
[0xB8] ALT
|
[0xB8] RALT // right Alt / AltGr -- see RALT above
|
||||||
};
|
};
|
||||||
|
|
||||||
static uchar togglecode[256] =
|
static uchar togglecode[256] =
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
#include "posix.h"
|
#include "posix.h"
|
||||||
|
|
||||||
// Six, and Linux's numbering: /dev/tty1 through /dev/tty6, reached with
|
// Six, and Linux's numbering: /dev/tty1 through /dev/tty6, reached with
|
||||||
// Alt+F1..Alt+F6. Alt+F7 is deliberately left alone -- that is where a
|
// Alt+1..Alt+6. A seventh is deliberately not bound -- that is where a
|
||||||
// display server goes, and Schcom will want it.
|
// display server goes, and Schcom will want it.
|
||||||
#define NVT 6
|
#define NVT 6
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,7 @@ erase_one(struct vt *v)
|
||||||
// Keyboard input
|
// Keyboard input
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Alt+F1..F6 switch terminals. kbd.c reports them as KEY_ALT_F1.. so that the
|
// Alt+1..6 switch terminals. kbd.c reports them as KEY_ALT_F1.. so that the
|
||||||
// decision of what a chord *means* is made here, where the terminals are,
|
// decision of what a chord *means* is made here, where the terminals are,
|
||||||
// rather than in a driver that should not know they exist.
|
// rather than in a driver that should not know they exist.
|
||||||
int
|
int
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,18 @@ kbdgetc(void)
|
||||||
shift |= shiftcode[data];
|
shift |= shiftcode[data];
|
||||||
shift ^= togglecode[data];
|
shift ^= togglecode[data];
|
||||||
|
|
||||||
// Alt+F1..F6 before the character tables, because there is no character to
|
// Alt+1..6 before the character tables, because there is no character to
|
||||||
// look up: it is a chord that means "show me a different terminal". What it
|
// look up: it is a chord that means "show me a different terminal". What it
|
||||||
// means is decided in console.c, which is where the terminals are; this only
|
// means is decided in console.c, which is where the terminals are; this only
|
||||||
// has to name it. See console_vt_hotkey().
|
// has to name it. See console_vt_hotkey().
|
||||||
if ((shift & ALT) && data >= SCAN_F1 && data < SCAN_F1 + NVT)
|
//
|
||||||
return KEY_ALT_F1 + (data - SCAN_F1);
|
// The modifier test is an equality, not a mask. `shift & ALT` would fire on
|
||||||
|
// Ctrl+Alt+1 and on AltGr+1 as well, so a chord aimed at the host -- or a
|
||||||
|
// Polish keystroke -- would switch terminals behind your back. Exactly left
|
||||||
|
// Alt, and nothing else held.
|
||||||
|
if ((shift & (CTL | SHIFT | ALT | RALT)) == ALT &&
|
||||||
|
data >= SCAN_1 && data < SCAN_1 + NVT)
|
||||||
|
return KEY_ALT_F1 + (data - SCAN_1);
|
||||||
|
|
||||||
c = charcode[shift & (CTL | SHIFT)][data];
|
c = charcode[shift & (CTL | SHIFT)][data];
|
||||||
if(shift & CAPSLOCK){
|
if(shift & CAPSLOCK){
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue