- SIGSTOP/SIGTSTP/SIGTTIN/SIGTTOU stop, SIGCONT resumes; the parent gets SIGCHLD and wait4 reports WUNTRACED/WCONTINUED; ps shows T. Ctrl-Z works, and mc no longer hangs waiting for its subshell to stop. - Blocking reads (console, pty, pipe) and sleeps take a pending stop and carry on after SIGCONT; a caught signal without SA_RESTART ends them with EINTR. - nanosleep reads tv_nsec (sleep 0.5 used to return at once), writes the remainder, and clock_nanosleep understands TIMER_ABSTIME. - rt_sigaction uses the k_sigaction layout for Linux-ABI processes: the old action's restorer was written 112 bytes past the caller's struct. Flags are kept; SIGKILL and SIGSTOP cannot be caught. - F1..F12 reach programs, as the Linux console sends them (PS/2, USB, I2C). - TIOCLINUX subcode 6 answers "no modifiers" instead of echoing the 6 back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
303 lines
12 KiB
C
303 lines
12 KiB
C
// Keyboard layouts. See include/dev/keymap.h for the interface and its limits.
|
||
//
|
||
// Every table is indexed by set-1 scancode, with the E0-prefixed keys folded
|
||
// into 0x80..0xFF by kbd.c before the lookup -- the same numbering the single
|
||
// hardcoded table used, so a layout is a diff against a shape that was already
|
||
// proven to work rather than a new mapping to get wrong.
|
||
//
|
||
// The tables are `uint32` and not `uchar`. That is the whole reason this file
|
||
// exists: `ą` is U+0105 and does not fit in a byte, and a Polish layout that
|
||
// cannot type `ą` is not one.
|
||
|
||
#include "core/types.h"
|
||
#include "core/defs.h"
|
||
#include "dev/kbd.h"
|
||
#include "dev/keymap.h"
|
||
|
||
// Shared by every layout that does not move the navigation block, which is
|
||
// all of them -- the arrow keys are not a national matter. Merged into each
|
||
// level table below rather than consulted separately, because a layout is
|
||
// allowed to disagree and none currently does.
|
||
#define NAV_KEYS \
|
||
[0xC8] = KBD_UP, [0xD0] = KBD_DN, \
|
||
[0xC9] = KBD_PGUP, [0xD1] = KBD_PGDN, \
|
||
[0xCB] = KBD_LF, [0xCD] = KBD_RT, \
|
||
[0x97] = KBD_HOME, [0xCF] = KBD_END, \
|
||
[0xD2] = KBD_INS, [0xD3] = KBD_DEL, \
|
||
[0x3B] = KBD_F1, [0x3C] = KBD_F1 + 1, \
|
||
[0x3D] = KBD_F1 + 2, [0x3E] = KBD_F1 + 3, \
|
||
[0x3F] = KBD_F1 + 4, [0x40] = KBD_F1 + 5, \
|
||
[0x41] = KBD_F1 + 6, [0x42] = KBD_F1 + 7, \
|
||
[0x43] = KBD_F1 + 8, [0x44] = KBD_F1 + 9, \
|
||
[0x57] = KBD_F1 + 10, [0x58] = KBD_F12, \
|
||
[0x9C] = '\n', /* KP_Enter */ \
|
||
[0xB5] = '/' /* KP_Div */
|
||
|
||
// --- US ---------------------------------------------------------------------
|
||
|
||
static const uint32 us_plain[256] = {
|
||
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
|
||
'7', '8', '9', '0', '-', '=', '\b', '\t',
|
||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
|
||
'o', 'p', '[', ']', '\n', NO, 'a', 's',
|
||
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
|
||
'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
|
||
'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
|
||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||
NAV_KEYS
|
||
};
|
||
|
||
static const uint32 us_shift[256] = {
|
||
NO, 0x1B, '!', '@', '#', '$', '%', '^', // 0x00
|
||
'&', '*', '(', ')', '_', '+', '\b', '\t',
|
||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
|
||
'O', 'P', '{', '}', '\n', NO, 'A', 'S',
|
||
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
|
||
'"', '~', NO, '|', 'Z', 'X', 'C', 'V',
|
||
'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
|
||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||
NAV_KEYS
|
||
};
|
||
|
||
// --- Polish (programmers) ---------------------------------------------------
|
||
//
|
||
// US QWERTY with nine letters on AltGr. This is the layout essentially every
|
||
// Polish developer uses, and the reason is that the alternative -- "Polish
|
||
// (214)", a genuine national arrangement -- moves Y, Z and most of the
|
||
// punctuation, which makes every shell and every editor shortcut land
|
||
// somewhere else. The diacritics go on AltGr and nothing else moves.
|
||
//
|
||
// AltGr+x is ź and AltGr+z is ż, which looks arbitrary and is not: the letter
|
||
// x is unused in Polish, so its key is free, and ź and ż both need one.
|
||
|
||
static const uint32 pl_altgr[256] = {
|
||
[0x1E] = 0x0105, // a -> ą
|
||
[0x2E] = 0x0107, // c -> ć
|
||
[0x12] = 0x0119, // e -> ę
|
||
[0x26] = 0x0142, // l -> ł
|
||
[0x31] = 0x0144, // n -> ń
|
||
[0x18] = 0x00F3, // o -> ó
|
||
[0x1F] = 0x015B, // s -> ś
|
||
[0x2D] = 0x017A, // x -> ź
|
||
[0x2C] = 0x017C, // z -> ż
|
||
};
|
||
|
||
static const uint32 pl_shiftaltgr[256] = {
|
||
[0x1E] = 0x0104, // A -> Ą
|
||
[0x2E] = 0x0106, // C -> Ć
|
||
[0x12] = 0x0118, // E -> Ę
|
||
[0x26] = 0x0141, // L -> Ł
|
||
[0x31] = 0x0143, // N -> Ń
|
||
[0x18] = 0x00D3, // O -> Ó
|
||
[0x1F] = 0x015A, // S -> Ś
|
||
[0x2D] = 0x0179, // X -> Ź
|
||
[0x2C] = 0x017B, // Z -> Ż
|
||
};
|
||
|
||
// --- German (QWERTZ) --------------------------------------------------------
|
||
//
|
||
// Y and Z swap, the umlauts and ß take the punctuation keys on the home and
|
||
// top rows, and the key between left Shift and Z -- scancode 0x56, which a US
|
||
// keyboard does not have at all -- carries < > |.
|
||
//
|
||
// The acute (0x0D) and circumflex (0x29) keys are dead on a real German
|
||
// layout. Here they type the accent on its own. See include/dev/keymap.h.
|
||
|
||
static const uint32 de_plain[256] = {
|
||
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
|
||
'7', '8', '9', '0', 0x00DF, 0x00B4, '\b', '\t', // ß ´
|
||
'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', // 0x10
|
||
'o', 'p', 0x00FC, '+', '\n', NO, 'a', 's', // ü
|
||
'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x00F6, // ö
|
||
0x00E4, '^', NO, '#', 'y', 'x', 'c', 'v', // ä
|
||
'b', 'n', 'm', ',', '.', '-', NO, '*', // 0x30
|
||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||
'2', '3', '0', ',', NO, NO, '<', NO, // 0x50, 0x56 = <>|
|
||
NAV_KEYS
|
||
};
|
||
|
||
static const uint32 de_shift[256] = {
|
||
NO, 0x1B, '!', '"', 0x00A7, '$', '%', '&', // 0x00 §
|
||
'/', '(', ')', '=', '?', '`', '\b', '\t',
|
||
'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', // 0x10
|
||
'O', 'P', 0x00DC, '*', '\n', NO, 'A', 'S', // Ü
|
||
'D', 'F', 'G', 'H', 'J', 'K', 'L', 0x00D6, // Ö
|
||
0x00C4, 0x00B0, NO, '\'', 'Y', 'X', 'C', 'V', // Ä °
|
||
'B', 'N', 'M', ';', ':', '_', NO, '*', // 0x30
|
||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||
'2', '3', '0', ',', NO, NO, '>', NO, // 0x50
|
||
NAV_KEYS
|
||
};
|
||
|
||
static const uint32 de_altgr[256] = {
|
||
[0x03] = 0x00B2, // 2 -> ²
|
||
[0x04] = 0x00B3, // 3 -> ³
|
||
[0x08] = '{', // 7
|
||
[0x09] = '[', // 8
|
||
[0x0A] = ']', // 9
|
||
[0x0B] = '}', // 0
|
||
[0x0C] = '\\', // ß
|
||
[0x10] = '@', // q
|
||
[0x12] = 0x20AC, // e -> €
|
||
[0x1B] = '~', // +
|
||
[0x32] = 0x00B5, // m -> µ
|
||
[0x56] = '|',
|
||
};
|
||
|
||
// --- the catalogue ----------------------------------------------------------
|
||
//
|
||
// Order matters only in that the first entry is the default. US, because a
|
||
// machine whose keyboard layout has never been set should behave the way the
|
||
// scancodes are printed on the majority of keyboards, and because every
|
||
// installation instruction anyone follows is written in ASCII.
|
||
|
||
static const struct keylayout layouts[] = {
|
||
{ "us", "English (US), QWERTY",
|
||
{ us_plain, us_shift, 0, 0 } },
|
||
{ "pl", "Polish (programmers) -- US QWERTY, diacritics on AltGr",
|
||
{ us_plain, us_shift, pl_altgr, pl_shiftaltgr } },
|
||
{ "de", "German, QWERTZ -- no dead keys, so the accent keys type the accent",
|
||
{ de_plain, de_shift, de_altgr, 0 } },
|
||
};
|
||
|
||
#define NLAYOUTS ((int)(sizeof(layouts) / sizeof(layouts[0])))
|
||
|
||
static const struct keylayout *active = &layouts[0];
|
||
|
||
const struct keylayout *
|
||
keymap_active(void)
|
||
{
|
||
return active;
|
||
}
|
||
|
||
const struct keylayout *
|
||
keymap_by_index(int i)
|
||
{
|
||
if (i < 0 || i >= NLAYOUTS)
|
||
return 0;
|
||
return &layouts[i];
|
||
}
|
||
|
||
int
|
||
keymap_set(const char *name)
|
||
{
|
||
for (int i = 0; i < NLAYOUTS; i++) {
|
||
const char *a = layouts[i].name, *b = name;
|
||
while (*a && *a == *b) { a++; b++; }
|
||
if (*a == 0 && *b == 0) {
|
||
active = &layouts[i];
|
||
return 0;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
// A control character from the plain codepoint rather than from a table of
|
||
// its own.
|
||
//
|
||
// The table this replaces was indexed by scancode, which quietly assumed a US
|
||
// keyboard: on QWERTZ, Ctrl+Z and Ctrl+Y would have swapped with the letters,
|
||
// so suspending a job would have depended on the layout. Deriving from what
|
||
// the key actually types is both shorter and correct everywhere.
|
||
static uint32
|
||
control_of(uint32 cp)
|
||
{
|
||
if (cp >= 'a' && cp <= 'z') return cp - 'a' + 1;
|
||
if (cp >= 'A' && cp <= 'Z') return cp - 'A' + 1;
|
||
switch (cp) {
|
||
case '@': case ' ': case '2': return 0; // NUL
|
||
case '[': return 0x1B; // ESC
|
||
case '\\': return 0x1C;
|
||
case ']': return 0x1D;
|
||
case '^': case '6': return 0x1E;
|
||
case '_': case '-': case '/': return 0x1F;
|
||
case '?': return 0x7F;
|
||
case '\n': case '\r': return '\r';
|
||
}
|
||
return 0; // no control character on this key
|
||
}
|
||
|
||
// Upper <-> lower for the letters these layouts can produce.
|
||
//
|
||
// Caps Lock needs this and Shift does not: Shift has its own table, so the
|
||
// layout has already said what the shifted key gives. Caps Lock has no table
|
||
// -- it applies to whatever came out, including `ą` from the AltGr level --
|
||
// so the case pair has to be derived from the codepoint.
|
||
//
|
||
// Latin Extended-A is laid out in alternating pairs, but not all the same way
|
||
// round: U+0100..U+0137 and U+014A..U+0177 have the capital on the even
|
||
// codepoint, while U+0139..U+0148 and U+0179..U+017E have it on the odd one.
|
||
// L-stroke is U+0141/U+0142 and Z-dot is U+017B/U+017C, so Polish lands in
|
||
// both halves and getting the parity wrong would shift one of them to a
|
||
// neighbouring letter rather than to its own capital.
|
||
static uint32
|
||
case_toggle(uint32 cp)
|
||
{
|
||
if (cp >= 'a' && cp <= 'z') return cp - 'a' + 'A';
|
||
if (cp >= 'A' && cp <= 'Z') return cp - 'A' + 'a';
|
||
|
||
// Latin-1: the accented letters run C0..DE and E0..FE, with multiplication
|
||
// and division signs punched out of the middle of each. Eszett (DF) has no
|
||
// single-codepoint capital and stays as it is.
|
||
if (cp >= 0xC0 && cp <= 0xDE && cp != 0xD7) return cp + 0x20;
|
||
if (cp >= 0xE0 && cp <= 0xFE && cp != 0xF7) return cp - 0x20;
|
||
|
||
if ((cp >= 0x0100 && cp <= 0x0137) || (cp >= 0x014A && cp <= 0x0177))
|
||
return (cp & 1) ? cp - 1 : cp + 1; // even is the capital
|
||
if ((cp >= 0x0139 && cp <= 0x0148) || (cp >= 0x0179 && cp <= 0x017E))
|
||
return (cp & 1) ? cp + 1 : cp - 1; // odd is the capital
|
||
|
||
return cp;
|
||
}
|
||
|
||
uint32
|
||
keymap_lookup(uint scancode, uint mods)
|
||
{
|
||
const struct keylayout *l = active;
|
||
uint32 cp;
|
||
|
||
if (scancode > 0xFF)
|
||
return 0;
|
||
|
||
// AltGr first. It is the only modifier that selects a whole different table,
|
||
// and on a layout that has no AltGr level the lookup falls back rather than
|
||
// producing nothing -- otherwise holding AltGr on a US keyboard would make
|
||
// every key dead.
|
||
int level = KM_PLAIN;
|
||
if (mods & RALT) level = (mods & SHIFT) ? KM_SHIFTALTGR : KM_ALTGR;
|
||
else if (mods & SHIFT) level = KM_SHIFT;
|
||
|
||
cp = l->level[level] ? l->level[level][scancode] : 0;
|
||
if (cp == 0 && level >= KM_ALTGR) {
|
||
level = (mods & SHIFT) ? KM_SHIFT : KM_PLAIN;
|
||
cp = l->level[level] ? l->level[level][scancode] : 0;
|
||
}
|
||
if (cp == 0)
|
||
return 0;
|
||
|
||
// Caps Lock is a letter-case toggle, not a Shift: it applies to whatever
|
||
// the table produced, including a letter that only exists on AltGr. `ą`
|
||
// with Caps Lock on is `Ą`, and only the layout knows that -- so the case
|
||
// pair has to come from the codepoint, not from a second table lookup.
|
||
if ((mods & CAPSLOCK) && cp < KBD_SPECIAL)
|
||
cp = case_toggle(cp);
|
||
|
||
// Ctrl is last and overrides: Ctrl+Shift+C is still Ctrl+C.
|
||
if (mods & CTL) {
|
||
if (cp >= KBD_SPECIAL)
|
||
return cp; // Ctrl+arrow stays an arrow
|
||
return control_of(cp);
|
||
}
|
||
|
||
return cp;
|
||
}
|