Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
175 lines
5.4 KiB
C
175 lines
5.4 KiB
C
#include "core/types.h"
|
|
#include "arch/x86.h"
|
|
#include "core/defs.h"
|
|
#include "dev/kbd.h"
|
|
#include "uapi/posix.h"
|
|
#include "dev/vt.h" /* NVT -- how many terminals the chords reach */
|
|
#include "dev/input.h"
|
|
#include "dev/keymap.h"
|
|
|
|
// The chord, settable at run time through VT_SETCHORD.
|
|
//
|
|
// AltGr+F1..F6 by default. Every other candidate is taken by the host, by
|
|
// applications, or by the keyboard layout -- see include/dev/kbd.h -- and AltGr
|
|
// with a function key is taken by none of the three: a Polish layout wants
|
|
// AltGr+*letter*, which still reaches the shell untouched, and no window
|
|
// manager binds AltGr with an F-key.
|
|
//
|
|
// AltGr+minus and AltGr+equals step to the previous and next terminal on the
|
|
// same modifiers, for when you have two and do not care which number they are.
|
|
//
|
|
// Zero modifiers disables all of it; `chvt N` still works.
|
|
uint vt_chord_mods = RALT;
|
|
uint vt_chord_base = SCAN_F1;
|
|
|
|
// Which scancodes are modifiers, and which are locks. Hardware facts rather
|
|
// than layout ones -- every layout agrees that 0x2A is a Shift key -- so they
|
|
// stay here while the character tables live in keymap.c.
|
|
static const uchar shiftcode[256] = {
|
|
[0x1D] = CTL,
|
|
[0x2A] = SHIFT,
|
|
[0x36] = SHIFT,
|
|
[0x38] = ALT,
|
|
[0x9D] = CTL,
|
|
[0xB8] = RALT // right Alt / AltGr -- see RALT in kbd.h
|
|
};
|
|
|
|
static const uchar togglecode[256] = {
|
|
[0x3A] = CAPSLOCK,
|
|
[0x45] = NUMLOCK,
|
|
[0x46] = SCROLLLOCK
|
|
};
|
|
|
|
// A key can produce more than one byte.
|
|
//
|
|
// consoleintr's contract with getc() is bytes: the serial line hands it raw
|
|
// UTF-8 already, and it would have no way to tell an encoded codepoint apart
|
|
// from a byte that happened to have the same value. So the keyboard encodes
|
|
// here and hands the bytes over one call at a time -- kbdintr loops until
|
|
// getc() returns negative, so a queue of at most four drains on its own
|
|
// before the next scancode is read.
|
|
static uchar pend[4];
|
|
static int pend_n, pend_i;
|
|
|
|
static int
|
|
queue_utf8(uint32 cp)
|
|
{
|
|
pend_i = 0;
|
|
if (cp < 0x80) {
|
|
pend_n = 0;
|
|
return (int)cp; // the common case: no queue at all
|
|
}
|
|
if (cp < 0x800) {
|
|
pend[0] = 0xC0 | (cp >> 6);
|
|
pend[1] = 0x80 | (cp & 0x3F);
|
|
pend_n = 2;
|
|
} else if (cp < 0x10000) {
|
|
pend[0] = 0xE0 | (cp >> 12);
|
|
pend[1] = 0x80 | ((cp >> 6) & 0x3F);
|
|
pend[2] = 0x80 | (cp & 0x3F);
|
|
pend_n = 3;
|
|
} else {
|
|
pend[0] = 0xF0 | (cp >> 18);
|
|
pend[1] = 0x80 | ((cp >> 12) & 0x3F);
|
|
pend[2] = 0x80 | ((cp >> 6) & 0x3F);
|
|
pend[3] = 0x80 | (cp & 0x3F);
|
|
pend_n = 4;
|
|
}
|
|
return pend[pend_i++];
|
|
}
|
|
|
|
int
|
|
kbdgetc(void)
|
|
{
|
|
static uint shift;
|
|
uint st, data;
|
|
uint32 cp;
|
|
|
|
if (pend_i < pend_n)
|
|
return pend[pend_i++]; // finish the last key first
|
|
pend_i = pend_n = 0;
|
|
|
|
st = inb(KBSTATP);
|
|
if((st & KBS_DIB) == 0)
|
|
return -1;
|
|
|
|
if(st & 0x20)
|
|
return -1;
|
|
|
|
data = inb(KBDATAP);
|
|
|
|
if(data == 0xE0){
|
|
shift |= E0ESC;
|
|
return 0;
|
|
}
|
|
|
|
// Feed /dev/input/event0 before the byte is turned into a character. This
|
|
// has to happen here rather than in a second handler because the byte can
|
|
// only be read from the port once -- whoever reads it owes it to everyone
|
|
// else. The console path below is untouched: both consumers see every key.
|
|
input_ps2_scancode(data, (shift & E0ESC) != 0);
|
|
|
|
// While something holds an exclusive grab on the keyboard, the console gets
|
|
// nothing. The shift/caps state below is still tracked, so releasing the
|
|
// grab does not leave the line discipline believing a modifier is held.
|
|
if(input_is_grabbed(INPUT_KBD)){
|
|
if(data & 0x80){
|
|
data = (shift & E0ESC ? data : data & 0x7F);
|
|
shift &= ~(shiftcode[data] | E0ESC);
|
|
} else {
|
|
shift |= shiftcode[data];
|
|
shift ^= togglecode[data];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if(data & 0x80){
|
|
data = (shift & E0ESC ? data : data & 0x7F);
|
|
shift &= ~(shiftcode[data] | E0ESC);
|
|
return 0;
|
|
} else if(shift & E0ESC){
|
|
data |= 0x80;
|
|
shift &= ~E0ESC;
|
|
}
|
|
|
|
shift |= shiftcode[data];
|
|
shift ^= togglecode[data];
|
|
|
|
// The switch chord, before the character tables, because there is no
|
|
// character to look up: it is a combination that means "show me a different
|
|
// terminal". What it means is decided in console.c, which is where the
|
|
// terminals are; this only has to name it. See console_vt_hotkey().
|
|
//
|
|
// 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 the
|
|
// modifiers named, and nothing else held.
|
|
if (vt_chord_mods != 0 &&
|
|
(shift & (CTL | SHIFT | ALT | RALT)) == vt_chord_mods) {
|
|
if (data >= vt_chord_base && data < vt_chord_base + NVT)
|
|
return KBD_VT_SWITCH + (data - vt_chord_base);
|
|
if (data == SCAN_MINUS) return KBD_VT_PREV;
|
|
if (data == SCAN_EQUALS) return KBD_VT_NEXT;
|
|
}
|
|
|
|
cp = keymap_lookup(data, shift);
|
|
if (cp == 0)
|
|
return 0;
|
|
if (cp >= KBD_SPECIAL)
|
|
return (int)cp; // arrows and the rest, not characters
|
|
|
|
return queue_utf8(cp);
|
|
}
|
|
|
|
void
|
|
kbdintr(void)
|
|
{
|
|
consoleintr(kbdgetc);
|
|
}
|
|
|
|
// The two headers describe the same bits to two different audiences, and a
|
|
// mismatch would be silent: a chord set from userspace would simply never
|
|
// match, and the only symptom would be a key that does nothing.
|
|
_Static_assert(VTC_SHIFT == SHIFT && VTC_CTRL == CTL &&
|
|
VTC_ALT == ALT && VTC_RALT == RALT,
|
|
"include/uapi/posix.h VTC_* must mirror include/dev/kbd.h modifiers");
|