diff --git a/kernel/core/console.c b/kernel/core/console.c index 099d000..2e97a19 100644 --- a/kernel/core/console.c +++ b/kernel/core/console.c @@ -442,7 +442,8 @@ erase_one(struct vt *v) // Keyboard input // --------------------------------------------------------------------------- -// Ctrl+Alt+Shift+F1..F6 switch terminals. kbd.c reports them as +// Alt+F1..F6, Ctrl+Alt+F1..F6 and the settable chord (AltGr+F1..F6 by +// default) switch terminals. kbd.c reports them as // KBD_VT_SWITCH+n so that the // decision of what a chord *means* is made here, where the terminals are, // rather than in a driver that should not know they exist. diff --git a/kernel/dev/kbd.c b/kernel/dev/kbd.c index 6acae66..f7cc3b5 100644 --- a/kernel/dev/kbd.c +++ b/kernel/dev/kbd.c @@ -152,6 +152,16 @@ kbdgetc(void) if (data == SCAN_EQUALS) return KBD_VT_NEXT; } + // And Linux's own: Alt+F1..F6 and Ctrl+Alt+F1..F6, whatever the chord is set + // to. These are what anyone who has used Linux presses, on a real machine, + // and nothing reached them: SchOS answered only AltGr+F-keys, which no one + // thinks to try. Under X the terminal is in VT_PROCESS, console_vt_hotkey() + // lets the keys through, and X switches with them itself -- as on Linux. + if (data >= SCAN_F1 && data < SCAN_F1 + NVT && + ((shift & (CTL | SHIFT | ALT | RALT)) == ALT || + (shift & (CTL | SHIFT | ALT | RALT)) == (CTL | ALT))) + return KBD_VT_SWITCH + (data - SCAN_F1); + cp = keymap_lookup(data, shift); if (cp == 0) return 0; diff --git a/kernel/drivers/usb/hid/usb_hid.c b/kernel/drivers/usb/hid/usb_hid.c index 569697e..df1a924 100644 --- a/kernel/drivers/usb/hid/usb_hid.c +++ b/kernel/drivers/usb/hid/usb_hid.c @@ -25,6 +25,8 @@ #include "core/dma.h" #include "dev/usb.h" #include "dev/input.h" +#include "dev/kbd.h" +#include "dev/vt.h" #define NHID 8 @@ -106,7 +108,7 @@ static const uchar hid_shifted[0x39] = { // which means the entire line discipline is reused rather than reimplemented. #define KQSIZE 64 static struct { - uchar buf[KQSIZE]; + int buf[KQSIZE]; // characters, and KBD_* codes past KBD_SPECIAL uint r, w; } kq; @@ -149,6 +151,32 @@ usb_kbd_getc(void) } // Was this usage in that report? +// The keys past the character table, as the console names them. The PS/2 +// path has always produced these (kernel/dev/kbd.c); this one skipped every +// usage from 0x39 up, so on a USB keyboard the console had no arrows, no +// Home/End/PgUp/PgDn/Delete, and no way to switch terminals. +static int +hid_special(uchar usage, uchar mods) +{ + // F1..F6 with Alt, AltGr or Ctrl+Alt: switch terminals, as on Linux. + if (usage >= 0x3A && usage < 0x3A + NVT && (mods & HID_MOD_ALT) && + !(mods & HID_MOD_SHIFT)) + return KBD_VT_SWITCH + (usage - 0x3A); + switch (usage) { + case 0x49: return KBD_INS; + case 0x4A: return KBD_HOME; + case 0x4B: return KBD_PGUP; + case 0x4C: return KBD_DEL; + case 0x4D: return KBD_END; + case 0x4E: return KBD_PGDN; + case 0x4F: return KBD_RT; + case 0x50: return KBD_LF; + case 0x51: return KBD_DN; + case 0x52: return KBD_UP; + } + return 0; +} + static int was_down(const uchar *prev, uchar usage) { @@ -222,18 +250,23 @@ keyboard_report(struct hid *h, const uchar *rep, int len) for(i = 2; i < 8; i++){ uchar usage = rep[i]; - if(usage == 0 || usage >= 0x39) + if(usage == 0) continue; if(was_down(h->prev, usage)) continue; // still held from last time, not a new press - c = (mods & HID_MOD_SHIFT) ? hid_shifted[usage] : hid_unshifted[usage]; - if(c == 0) - continue; + if(usage >= 0x39){ + if((c = hid_special(usage, mods)) == 0) + continue; + } else { + c = (mods & HID_MOD_SHIFT) ? hid_shifted[usage] : hid_unshifted[usage]; + if(c == 0) + continue; + } // Ctrl folds a letter down into its control character, which is how ^C // and ^D reach the line discipline at all. - if(mods & HID_MOD_CTRL){ + if((mods & HID_MOD_CTRL) && c < KBD_SPECIAL){ if(c >= 'a' && c <= 'z') c = c - 'a' + 1; else if(c >= 'A' && c <= 'Z') @@ -242,6 +275,8 @@ keyboard_report(struct hid *h, const uchar *rep, int len) continue; } kq_put(c); + if(c >= KBD_VT_SWITCH && c < KBD_VT_SWITCH + NVT) + continue; // a held chord switches once // The newest key is the one that repeats, as on any keyboard. acquire(&kqlock); diff --git a/kernel/sys/sys_fs.c b/kernel/sys/sys_fs.c index 4305d47..1f6eaff 100644 --- a/kernel/sys/sys_fs.c +++ b/kernel/sys/sys_fs.c @@ -70,6 +70,22 @@ linux_makedev(uint32 major, uint32 minor) | ((uint64)(major & ~0xfffu) << 32) | ((uint64)(minor & ~0xffu) << 12); } +// Terminals as Linux numbers them: tty0..63 are (4, n), /dev/tty is (5, 0) and +// /dev/console (5, 1). SchOS keeps them all under major 1 (kernel/fs/devfs.c), +// and Linux programs look at the number rather than the name. Xorg is the one +// that matters: it uses VTs only if stat("/dev/tty0") says major 4 +// (xf86HasTTYs), and otherwise runs on none -- no VT switch, no KD_GRAPHICS, +// the console drawing over it, and Ctrl+Alt+F-keys doing nothing. +static uint64 +linux_tty_rdev(uint32 major, uint32 minor) +{ + if (major == 1 && minor == 0) return linux_makedev(4, 0); + if (major == 1 && minor >= 11 && minor <= 16) return linux_makedev(4, minor - 10); + if (major == 1 && minor == 10) return linux_makedev(5, 0); + if (major == 1 && minor == 1) return linux_makedev(5, 1); + return linux_makedev(major, minor); +} + static void stati_linux(struct inode *ip, struct linux_stat *st) { @@ -102,7 +118,7 @@ stati_linux(struct inode *ip, struct linux_stat *st) // entry in /dev printed as "0, 0" under ls -l -- harmless while nothing // read it, and wrong the moment something has to tell two disks apart. if (ip->type == T_DEV) - st->st_rdev = linux_makedev(ip->major, ip->minor); + st->st_rdev = linux_tty_rdev(ip->major, ip->minor); st->st_atime = ip->atime; st->st_mtime = ip->mtime; st->st_ctime = ip->ctime; } diff --git a/tools/posix/mkaroot.sh b/tools/posix/mkaroot.sh index bd233f4..4d2e71e 100644 --- a/tools/posix/mkaroot.sh +++ b/tools/posix/mkaroot.sh @@ -106,7 +106,10 @@ rm -f "$P/etc/profile" mkdir -p "$P/root" cat > "$P/root/.xserverrc" <<'XEOF' #!/bin/sh -exec /usr/bin/X -nolisten tcp -dumbSched "$@" +# The VT X runs on is the one startx was started from. startx works that out +# only when `uname -s` says Linux, and SchOS's says SchIFT. +vt=$(tty | sed -n 's|^/dev/tty\([0-9][0-9]*\)$|vt\1|p') +exec /usr/bin/X -nolisten tcp -dumbSched $vt "$@" XEOF # What startx runs inside it: Window Maker, which world installs. Without this @@ -116,6 +119,55 @@ cat > "$P/root/.xinitrc" <<'XEOF' exec wmaker XEOF +# X's input devices, named rather than found. Alpine's Xorg asks udev for +# them, SchOS has no udev, and X came up with no keyboard and no mouse: the +# pointer did not move, Ctrl+Alt+F-keys did nothing and nothing could end the +# session. SchOS has exactly two input nodes whatever the machine -- every +# keyboard on event0, every mouse on event1 (kernel/dev/input.c) -- so a +# fixed list is the whole answer. The file is SchOS's, in xorg.conf.d, which +# no Alpine package writes into. +mkdir -p "$P/etc/X11/xorg.conf.d" +cat > "$P/etc/X11/xorg.conf.d/10-schos.conf" <<'XEOF' +# SchOS has no udev, so X is told its input devices instead of finding them. +# event0 is every keyboard and event1 every mouse (kernel/dev/input.c). +Section "ServerFlags" + Option "AutoAddDevices" "false" + # Ctrl+Alt+Backspace ends X. + Option "DontZap" "false" +EndSection + +Section "InputDevice" + Identifier "keyboard" + Driver "evdev" + Option "Device" "/dev/input/event0" + Option "XkbRules" "evdev" + Option "XkbOptions" "terminate:ctrl_alt_bksp" +EndSection + +Section "InputDevice" + Identifier "mouse" + Driver "evdev" + Option "Device" "/dev/input/event1" +EndSection + +Section "Device" + Identifier "fb" + Driver "fbdev" +EndSection + +Section "Screen" + Identifier "screen" + Device "fb" +EndSection + +Section "ServerLayout" + Identifier "schos" + Screen "screen" + InputDevice "keyboard" "CoreKeyboard" + InputDevice "mouse" "CorePointer" +EndSection +XEOF + # Mount points and runtime directories are alpine-baselayout's; an empty # directory here only duplicates it. for d in dev proc sys run tmp srv; do