#include "types.h" #include "x86.h" #include "defs.h" #include "kbd.h" #include "posix.h" #include "vt.h" /* NVT -- how many terminals the chords reach */ #include "input.h" #include "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/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/posix.h VTC_* must mirror include/kbd.h modifiers");