From 4a7165c951ffbe432f2f2120d024e07fd3704a00 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Sat, 24 Oct 2026 21:45:50 +0200 Subject: [PATCH] X gets a VT: keyboard, terminal switching, and a way out Xorg uses VTs only when stat("/dev/tty0") reports Linux's tty major 4. SchOS's terminals sit under major 1, so X ran on no VT at all: the console kept drawing over it, Ctrl+Alt+F-keys did nothing, and nothing ended the session. stat now reports terminals under Linux's numbers. startx picks the VT only when uname says Linux, so root's xserverrc passes the current one itself. An xorg.conf.d file names X's input devices, since Alpine's Xorg looks them up through udev and SchOS has none yet. Alt+F1..F6 and Ctrl+Alt+F1..F6 now switch terminals as on Linux, alongside the settable chord. USB keyboards gain arrows, Home/End/PgUp/PgDn/Ins/Del and the switch keys, which the PS/2 path always had. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4 --- kernel/core/console.c | 3 +- kernel/dev/kbd.c | 10 ++++++ kernel/drivers/usb/hid/usb_hid.c | 47 +++++++++++++++++++++++---- kernel/sys/sys_fs.c | 18 ++++++++++- tools/posix/mkaroot.sh | 54 +++++++++++++++++++++++++++++++- 5 files changed, 123 insertions(+), 9 deletions(-) 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