feat: extend kmain with injectKeyPress and injectShiftedKeyPress
This commit is contained in:
1 parent
b4f8d5215f
commit
750354f099
1 file changed
+173
+173
@@ -504,6 +504,179 @@ static void pgroupInjectorThread(void)
|
|||||||
#define scancodeMakeP 0x19
|
#define scancodeMakeP 0x19
|
||||||
#define scancodeMakeI 0x17
|
#define scancodeMakeI 0x17
|
||||||
#define scancodeMakeE 0x12
|
#define scancodeMakeE 0x12
|
||||||
|
#define scancodeMakeS 0x1F
|
||||||
|
#define scancodeMakeR 0x13
|
||||||
|
#define scancodeMakeN 0x31
|
||||||
|
#define scancodeMakeK 0x25
|
||||||
|
#define scancodeMakeBackslash 0x2B
|
||||||
|
|
||||||
|
static void injectKeyPress(uint8 makeCode)
|
||||||
|
{
|
||||||
|
keyboardHandleScancode(makeCode);
|
||||||
|
keyboardHandleScancode(makeCode | scancodeBreakBit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* '&' is shift+7 on a real US keyboard -- keyboard.c's scancodeUpper table
|
||||||
|
maps index scancodeMakeSeven to '&' only while shiftHeld is true. */
|
||||||
|
static void injectShiftedKeyPress(uint8 makeCode)
|
||||||
|
{
|
||||||
|
keyboardHandleScancode(scancodeLeftShiftMake);
|
||||||
|
keyboardHandleScancode(makeCode);
|
||||||
|
keyboardHandleScancode(makeCode | scancodeBreakBit);
|
||||||
|
keyboardHandleScancode(scancodeLeftShiftBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A single bare Enter, unconditionally, before typing anything this thread
|
||||||
|
actually cares about: every keystroke keyboardHandleScancode decodes is
|
||||||
|
broadcast to EVERY open console reader (keyboard.c), the shell's own
|
||||||
|
included, so consoleInjectorThread's single synthetic 'k' (well before
|
||||||
|
this thread ever runs) is still sitting unflushed in the shell's own
|
||||||
|
line buffer -- there is no way for this thread to tell "a stray
|
||||||
|
character from an earlier, unrelated demo" apart from "something a real
|
||||||
|
user typed". One Enter here executes that "k" as a harmless unknown
|
||||||
|
command (shell.c's execute()) and guarantees an empty line buffer before
|
||||||
|
the real commands below, exactly like pressing Enter at a real prompt to
|
||||||
|
clear a garbled line. */
|
||||||
|
static void jobctlTypeFlushLine(void)
|
||||||
|
{
|
||||||
|
injectKeyPress(scancodeMakeEnter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jobctlTypeBackgroundCommand(void)
|
||||||
|
{
|
||||||
|
injectKeyPress(scancodeMakeJ);
|
||||||
|
injectKeyPress(scancodeMakeO);
|
||||||
|
injectKeyPress(scancodeMakeB);
|
||||||
|
injectKeyPress(scancodeCPressMake);
|
||||||
|
injectKeyPress(scancodeMakeT);
|
||||||
|
injectKeyPress(scancodeMakeL);
|
||||||
|
injectKeyPress(scancodeMakeB);
|
||||||
|
injectKeyPress(scancodeMakeG);
|
||||||
|
injectKeyPress(scancodeMakeSpace);
|
||||||
|
injectShiftedKeyPress(scancodeMakeSeven);
|
||||||
|
injectKeyPress(scancodeMakeEnter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jobctlTypeForegroundCommand(void)
|
||||||
|
{
|
||||||
|
injectKeyPress(scancodeMakeJ);
|
||||||
|
injectKeyPress(scancodeMakeO);
|
||||||
|
injectKeyPress(scancodeMakeB);
|
||||||
|
injectKeyPress(scancodeCPressMake);
|
||||||
|
injectKeyPress(scancodeMakeT);
|
||||||
|
injectKeyPress(scancodeMakeL);
|
||||||
|
injectKeyPress(scancodeMakeF);
|
||||||
|
injectKeyPress(scancodeMakeG);
|
||||||
|
injectKeyPress(scancodeMakeEnter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define jobctlDriverInitialDelayMs 900
|
||||||
|
#define jobctlDriverTypeSettleMs 150
|
||||||
|
#define jobctlDriverForegroundSettleMs 350
|
||||||
|
#define jobctlDriverSignalSettleMs 150
|
||||||
|
|
||||||
|
static void jobctlDriverThread(void)
|
||||||
|
{
|
||||||
|
threadSleep(jobctlDriverInitialDelayMs);
|
||||||
|
while (currentThread()->state == threadSleeping)
|
||||||
|
halt();
|
||||||
|
|
||||||
|
kprintf("[kbd] typing shell commands for the on-demand job-control demo\n");
|
||||||
|
jobctlTypeFlushLine();
|
||||||
|
|
||||||
|
/* "jobctlbg &" -- backgrounded, so shell.c's runProgram never blocks
|
||||||
|
on it: jobctlSpawnForeground/jobctlRunBackground's own load+spawn
|
||||||
|
work is synchronous kernel code, so by the time the shell has
|
||||||
|
actually consumed all of these buffered bytes (needs its own
|
||||||
|
timeslice; keyboardHandleScancode above only enqueues them) its
|
||||||
|
shellLastSpawnedPid() is already this job's real pid, not stale. */
|
||||||
|
jobctlTypeBackgroundCommand();
|
||||||
|
threadSleep(jobctlDriverTypeSettleMs);
|
||||||
|
while (currentThread()->state == threadSleeping)
|
||||||
|
halt();
|
||||||
|
uint32 jobctlbgPid = shellLastSpawnedPid();
|
||||||
|
|
||||||
|
/* "jobctlfg" -- foreground, so the shell thread itself parks inside
|
||||||
|
jobctlWaitForeground (threadJoin) the moment it processes this
|
||||||
|
line's own trailing Enter. shellLastSpawnedPid() is still safe to
|
||||||
|
read afterward: shell.c's runProgram sets it right after
|
||||||
|
jobctlSpawnForeground returns, BEFORE ever calling
|
||||||
|
jobctlWaitForeground, precisely so a live demo like this one is
|
||||||
|
never stuck waiting for a foreground job to exit before it can even
|
||||||
|
learn that job's own pid. The longer settle here (vs. the
|
||||||
|
background command above) gives jobctlfg.c itself real scheduler
|
||||||
|
time to reach its own blocking pipe read before Ctrl-C below --
|
||||||
|
the same margin ratio pgroupInjectDelayMs/pgroupSettleDelayMs above
|
||||||
|
already established for an equivalent single-process case. */
|
||||||
|
jobctlTypeForegroundCommand();
|
||||||
|
threadSleep(jobctlDriverForegroundSettleMs);
|
||||||
|
while (currentThread()->state == threadSleeping)
|
||||||
|
halt();
|
||||||
|
uint32 jobctlfgPid = shellLastSpawnedPid();
|
||||||
|
|
||||||
|
if (jobctlbgPid == threadInvalidId || jobctlfgPid == threadInvalidId ||
|
||||||
|
jobctlbgPid == jobctlfgPid) {
|
||||||
|
kprintf("[kbd] on-demand job-control demo skipped: jobctlbg/jobctlfg never started\n");
|
||||||
|
for (;;)
|
||||||
|
halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
kprintf("[kbd] injecting synthetic ctrl-c for the on-demand job-control demo\n");
|
||||||
|
keyboardHandleScancode(scancodeLeftCtrlMake);
|
||||||
|
keyboardHandleScancode(scancodeCPressMake);
|
||||||
|
keyboardHandleScancode(scancodeCPressBreak);
|
||||||
|
keyboardHandleScancode(scancodeLeftCtrlBreak);
|
||||||
|
|
||||||
|
/* Give the scheduler a real timeslice to actually deliver the
|
||||||
|
resulting sigInt to jobctlfg (early-wake + return-to-ring3), let
|
||||||
|
the now-parked shell thread's own threadJoin notice it Dead, and let
|
||||||
|
jobctlWaitForeground's reclaim (jobctl.c) run -- all real scheduled
|
||||||
|
work, not instantaneous. */
|
||||||
|
threadSleep(jobctlDriverSignalSettleMs);
|
||||||
|
while (currentThread()->state == threadSleeping)
|
||||||
|
halt();
|
||||||
|
|
||||||
|
/* The direct, causal proof that shell.c's runProgram/jobctl.c's
|
||||||
|
jobctlWaitForeground actually reclaimed the terminal for the shell's
|
||||||
|
OWN group the instant jobctlfg died, rather than leaving it
|
||||||
|
permanently pointed at a now-dead pgid the way nothing in this
|
||||||
|
kernel ever un-claimed it before this subsystem existed: if the
|
||||||
|
reclaim never ran (or ran against the wrong pgid), the foreground
|
||||||
|
would still equal jobctlfg's own now-meaningless pid forever. */
|
||||||
|
uint32 fgAfterReclaim = keyboardForegroundPgid();
|
||||||
|
bool reclaimed = (fgAfterReclaim != jobctlfgPid);
|
||||||
|
kprintf("[user] jobctl demo: jobctlfg pid=%u foreground pgid now=%u -> %s\n",
|
||||||
|
jobctlfgPid, fgAfterReclaim,
|
||||||
|
reclaimed ? "shell-foreground-reclaimed-ok" : "FAIL");
|
||||||
|
|
||||||
|
/* Proves jobctlbg (never granted the terminal at all, per shell.c's
|
||||||
|
`&` handling) is still genuinely alive and running real code after
|
||||||
|
the Ctrl-C above -- if the shell's background path ever put it in
|
||||||
|
jobctlfg's group (or granted it the foreground itself), it would
|
||||||
|
already be Dead by now and this sigRaise would silently do nothing,
|
||||||
|
so jobctlbg.c's own "still-alive-after-ctrlc-ok" needle would simply
|
||||||
|
never appear in the boot log, failing the gate. */
|
||||||
|
sigRaise(jobctlbgPid, sigUsr1);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define pipelineDriverInitialDelayMs 2500
|
||||||
|
#define pipelineDriverSettleMs 500
|
||||||
|
|
||||||
|
static void pipelineTypeCommand(void)
|
||||||
|
{
|
||||||
|
injectKeyPress(scancodeMakeP);
|
||||||
|
injectKeyPress(scancodeMakeI);
|
||||||
|
injectKeyPress(scancodeMakeP);
|
||||||
|
injectKeyPress(scancodeMakeE);
|
||||||
|
injectKeyPress(scancodeMakeS);
|
||||||
|
injectKeyPress(scancodeMakeR);
|
||||||
|
injectKeyPress(scancodeCPressMake);
|
||||||
|
injectKeyPress(scancodeMakeSpace);
|
||||||
|
injectShiftedKeyPress(scancodeMakeBackslash);
|
||||||
|
injectKeyPress(scancodeMakeSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kmain(uint32 magic, uint64 multibootInfo)
|
void kmain(uint32 magic, uint64 multibootInfo)
|
||||||
|
|||||||
Reference in new issue
Block a user