Files
nt-lite/code/kernel/ke_input.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

698 lines
25 KiB
C

// Kernel input core. This file owns canonical keyboard and mouse state shared by input drivers and syscalls.
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <intrin.h>
#include <kernel/ke_input.h>
#include <kernel/ke_id.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_display.h>
#include <kernel/ke_event.h>
#include <kernel/ke_dpc.h>
#include <kernel/ob_object.h>
#include <ntlite/syscall.h>
#define KEY_QUEUE_SIZE 256u
#define KEY_QUEUE_MASK (KEY_QUEUE_SIZE - 1u)
#define MOUSE_QUEUE_SIZE 128u
#define MOUSE_QUEUE_MASK (MOUSE_QUEUE_SIZE - 1u)
#define MANAGER_QUEUE_SIZE 256u
#define MANAGER_QUEUE_MASK (MANAGER_QUEUE_SIZE - 1u)
#define MAX_INPUT_KEYS 256u
#define KEY_REPEAT_DELAY_US 500000ull
#define KEY_REPEAT_PERIOD_US 33000ull
typedef struct KEY_CHAR_PAIR {
INPUT_KEY key;
char normal;
char shifted;
} KEY_CHAR_PAIR;
static KE_INPUT_CONTEXT gDefaultInputContext;
static KE_INPUT_MANAGER gInputManager;
static KE_ID_ALLOCATOR gInputDeviceIds = KE_ID_ALLOCATOR_INIT(1u);
static char KiInputKeyToChar(KE_INPUT_CONTEXT* context, INPUT_KEY key);
static void KiInputServiceRepeat(KE_INPUT_CONTEXT* context);
// Advances the physical input snapshot generation after visible state changes.
static void KiInputBumpGeneration(KE_INPUT_CONTEXT* context) {
_InterlockedIncrement64(&context->Generation);
}
static const KEY_CHAR_PAIR gCharMap[] = {
{ INPUT_KEY_1, '1', '!' }, { INPUT_KEY_2, '2', '@' },
{ INPUT_KEY_3, '3', '#' }, { INPUT_KEY_4, '4', '$' },
{ INPUT_KEY_5, '5', '%' }, { INPUT_KEY_6, '6', '^' },
{ INPUT_KEY_7, '7', '&' }, { INPUT_KEY_8, '8', '*' },
{ INPUT_KEY_9, '9', '(' }, { INPUT_KEY_0, '0', ')' },
{ INPUT_KEY_MINUS, '-', '_' }, { INPUT_KEY_EQUAL, '=', '+' },
{ INPUT_KEY_LEFT_BRACKET, '[', '{' }, { INPUT_KEY_RIGHT_BRACKET, ']', '}' },
{ INPUT_KEY_BACKSLASH, '\\', '|' }, { INPUT_KEY_SEMICOLON, ';', ':' },
{ INPUT_KEY_APOSTROPHE, '\'', '"' }, { INPUT_KEY_GRAVE, '`', '~' },
{ INPUT_KEY_COMMA, ',', '<' }, { INPUT_KEY_PERIOD, '.', '>' },
{ INPUT_KEY_SLASH, '/', '?' },
{ INPUT_KEY_SPACE, ' ', ' ' }, { INPUT_KEY_TAB, '\t', '\t' },
{ INPUT_KEY_ENTER, '\n', '\n' }, { INPUT_KEY_BACKSPACE, '\b', '\b' },
{ INPUT_KEY_KP_DIVIDE, '/', '/' }, { INPUT_KEY_KP_MULTIPLY, '*', '*' },
{ INPUT_KEY_KP_MINUS, '-', '-' }, { INPUT_KEY_KP_PLUS, '+', '+' },
{ INPUT_KEY_KP_ENTER, '\n', '\n' }, { INPUT_KEY_KP_1, '1', '1' },
{ INPUT_KEY_KP_2, '2', '2' }, { INPUT_KEY_KP_3, '3', '3' },
{ INPUT_KEY_KP_4, '4', '4' }, { INPUT_KEY_KP_5, '5', '5' },
{ INPUT_KEY_KP_6, '6', '6' }, { INPUT_KEY_KP_7, '7', '7' },
{ INPUT_KEY_KP_8, '8', '8' }, { INPUT_KEY_KP_9, '9', '9' },
{ INPUT_KEY_KP_0, '0', '0' }, { INPUT_KEY_KP_PERIOD, '.', '.' },
};
// Updates the waitable manager event to match queued physical input.
static void KiInputRefreshManagerEvent(KE_INPUT_CONTEXT* context) {
if (!context->ManagerWaitEvent) return;
if (context->ManagerHead != context->ManagerTail) KeSignalEvent(context->ManagerWaitEvent);
else KeClearEvent(context->ManagerWaitEvent);
}
// Queues one normalized physical event for the delegated input manager.
static void KiInputQueueManagerEvent(KE_INPUT_CONTEXT* context, const KE_INPUT_MANAGER_EVENT* event) {
if (!context || !event) return;
int next = (context->ManagerTail + 1) & (int)MANAGER_QUEUE_MASK;
if (next == context->ManagerHead) return;
KE_INPUT_MANAGER_EVENT queued = *event;
queued.Sequence = (uint64_t)_InterlockedIncrement64(&context->ManagerSequence);
context->ManagerQueue[context->ManagerTail] = queued;
context->ManagerTail = next;
KiInputRefreshManagerEvent(context);
}
// Updates the waitable keyboard event to match queued keyboard input.
static void KiInputRefreshKeyboardEvent(KE_INPUT_CONTEXT* context) {
if (!context->WaitEvent) return;
if (context->KeyHead != context->KeyTail) KeSignalEvent(context->WaitEvent);
else KeClearEvent(context->WaitEvent);
}
// Queues a keyboard event and wakes waiters when the event was accepted.
static void KiInputQueueKey(KE_INPUT_CONTEXT* context, INPUT_KEY key, char ch) {
if (key == INPUT_KEY_NONE) return;
int next = (context->KeyTail + 1) & (int)KEY_QUEUE_MASK;
if (next == context->KeyHead) return;
context->KeyQueue[context->KeyTail].Key = key;
context->KeyQueue[context->KeyTail].Character = ch;
context->KeyQueue[context->KeyTail].Modifiers = context->Modifiers;
context->KeyTail = next;
KiInputRefreshKeyboardEvent(context);
}
// Implements the input Queue Has Space operation.
static bool KiInputQueueHasSpace(KE_INPUT_CONTEXT* context) {
int next = (context->KeyTail + 1) & (int)KEY_QUEUE_MASK;
return next != context->KeyHead;
}
// Implements the input Key Is Repeatable operation.
static bool KiInputKeyIsRepeatable(INPUT_KEY key) {
if (key == INPUT_KEY_NONE) return false;
switch (key) {
case INPUT_KEY_LEFT_SHIFT:
case INPUT_KEY_RIGHT_SHIFT:
case INPUT_KEY_LEFT_CTRL:
case INPUT_KEY_RIGHT_CTRL:
case INPUT_KEY_LEFT_ALT:
case INPUT_KEY_RIGHT_ALT:
case INPUT_KEY_LEFT_GUI:
case INPUT_KEY_RIGHT_GUI:
case INPUT_KEY_CAPS_LOCK:
case INPUT_KEY_NUM_LOCK:
return false;
default:
return true;
}
}
// Implements the input Now Ticks operation.
static uint64_t KiInputNowTicks(KE_INPUT_CONTEXT* context) {
if (context->Hal && context->Hal->Timer.QueryTicks)
return context->Hal->Timer.QueryTicks();
return 0;
}
// Implements the input Start Repeat operation.
static void KiInputStartRepeat(KE_INPUT_CONTEXT* context, INPUT_KEY key, uint64_t now) {
if (!KiInputKeyIsRepeatable(key)) return;
context->RepeatKey = key;
context->RepeatNextTick = now + KEY_REPEAT_DELAY_US;
}
// Implements the input Stop Repeat operation.
static void KiInputStopRepeat(KE_INPUT_CONTEXT* context, INPUT_KEY key) {
if (context->RepeatKey == key) {
context->RepeatKey = INPUT_KEY_NONE;
context->RepeatNextTick = 0;
}
}
// Implements the input Service Repeat operation.
static void KiInputServiceRepeat(KE_INPUT_CONTEXT* context) {
INPUT_KEY key = context->RepeatKey;
if (!KiInputKeyIsRepeatable(key) || key >= MAX_INPUT_KEYS || !context->KeyDown[key]) {
context->RepeatKey = INPUT_KEY_NONE;
context->RepeatNextTick = 0;
return;
}
uint64_t now = KiInputNowTicks(context);
if (now == 0 || now < context->RepeatNextTick)
return;
if (!KiInputQueueHasSpace(context)) {
context->RepeatNextTick = now + KEY_REPEAT_PERIOD_US;
return;
}
KiInputQueueKey(context, key, KiInputKeyToChar(context, key));
context->RepeatNextTick = now + KEY_REPEAT_PERIOD_US;
}
// Implements the input Repeat Dpc Routine operation.
static void KiInputRepeatDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
(void)dpc;
(void)systemArgument1;
(void)systemArgument2;
KiInputServiceRepeat((KE_INPUT_CONTEXT*)context);
}
// Implements the input Set Modifier operation.
static void KiInputSetModifier(KE_INPUT_CONTEXT* context, INPUT_KEY key, bool pressed) {
uint32_t bit = 0;
switch (key) {
case INPUT_KEY_LEFT_SHIFT: bit = INPUT_MOD_LSHIFT; break;
case INPUT_KEY_RIGHT_SHIFT: bit = INPUT_MOD_RSHIFT; break;
case INPUT_KEY_LEFT_CTRL: bit = INPUT_MOD_LCTRL; break;
case INPUT_KEY_RIGHT_CTRL: bit = INPUT_MOD_RCTRL; break;
case INPUT_KEY_LEFT_ALT: bit = INPUT_MOD_LALT; break;
case INPUT_KEY_RIGHT_ALT: bit = INPUT_MOD_RALT; break;
case INPUT_KEY_LEFT_GUI: bit = INPUT_MOD_LGUI; break;
case INPUT_KEY_RIGHT_GUI: bit = INPUT_MOD_RGUI; break;
default: break;
}
if (bit) {
if (pressed) context->Modifiers |= bit;
else context->Modifiers &= ~bit;
}
if (pressed && key == INPUT_KEY_CAPS_LOCK) context->Modifiers ^= INPUT_MOD_CAPS_LOCK;
if (pressed && key == INPUT_KEY_NUM_LOCK) context->Modifiers ^= INPUT_MOD_NUM_LOCK;
}
// Implements the input Key To Char operation.
static char KiInputKeyToChar(KE_INPUT_CONTEXT* context, INPUT_KEY key) {
if (key >= INPUT_KEY_A && key <= INPUT_KEY_Z) {
bool shifted = (context->Modifiers & INPUT_MOD_SHIFT) != 0;
bool caps = (context->Modifiers & INPUT_MOD_CAPS_LOCK) != 0;
char c = (char)('a' + (key - INPUT_KEY_A));
if (shifted ^ caps) c = (char)(c - 'a' + 'A');
return c;
}
bool shifted = (context->Modifiers & INPUT_MOD_SHIFT) != 0;
for (size_t i = 0; i < sizeof(gCharMap) / sizeof(gCharMap[0]); i++) {
if (gCharMap[i].key == key)
return shifted ? gCharMap[i].shifted : gCharMap[i].normal;
}
return 0;
}
// Implements the input Initialize operation.
void KeInputInitialize(const HAL_API* hal) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
ObInitializeObjectHeader(&gInputManager.Header, ObjectTypeInputManager);
context->Hal = hal;
memset((void*)context->KeyDown, 0, sizeof(context->KeyDown));
context->Modifiers = 0;
context->Generation = 0;
context->KeyHead = context->KeyTail = 0;
context->RepeatKey = INPUT_KEY_NONE;
context->RepeatNextTick = 0;
context->MouseHead = context->MouseTail = 0;
context->ManagerHead = context->ManagerTail = 0;
context->ManagerSequence = 0;
context->MouseBoundsWidth = 1920;
context->MouseBoundsHeight = 1080;
context->MouseWheel = 0;
context->MouseButtons = 0;
KeInitializeDpc(&context->RepeatDpc, KiInputRepeatDpcRoutine, context);
if (!context->WaitEvent) {
KSTATUS s = KeEventCreate(EventManualReset, 0, &context->WaitEvent);
if (!KSUCCESS(s))
DbgPrint("Input: keyboard wait event create failed st=0x%x\n", s);
}
if (!context->ManagerWaitEvent) {
KSTATUS s = KeEventCreate(EventManualReset, 0, &context->ManagerWaitEvent);
if (!KSUCCESS(s))
DbgPrint("Input: manager wait event create failed st=0x%x\n", s);
}
KiInputRefreshKeyboardEvent(context);
KiInputRefreshManagerEvent(context);
int w = 1920, h = 1080;
DISPLAY_MODE mode;
if (KSUCCESS(KeDisplayGetMode(&mode))) {
w = (int)mode.Width;
h = (int)mode.Height;
}
KeInputMouseSetBounds(w, h);
KeInputMouseSetPosition(w / 2, h / 2);
KeDisplayShowCursor(true);
DbgPrint("Input: kernel input core initialized (%dx%d)\n", w, h);
}
// References the singleton input-manager authority object used for bootstrap delegation.
KSTATUS KeInputReferenceManager(KE_INPUT_MANAGER** managerOut) {
if (!managerOut) return NTLSTATUS_INVALID_PARAMETER;
*managerOut = &gInputManager;
ObReferenceObject(&gInputManager.Header);
return NTLSTATUS_SUCCESS;
}
// Reads one normalized physical event from the singleton manager stream.
KSTATUS KeInputManagerReadEvent(KE_INPUT_MANAGER* manager, uint64_t timeoutMs, SYS_INPUT_MANAGER_EVENT* eventOut) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (!manager || manager != &gInputManager || !eventOut)
return NTLSTATUS_INVALID_PARAMETER;
for (;;) {
if (context->ManagerHead != context->ManagerTail) {
KE_INPUT_MANAGER_EVENT event = context->ManagerQueue[context->ManagerHead];
context->ManagerHead = (context->ManagerHead + 1) & (int)MANAGER_QUEUE_MASK;
KiInputRefreshManagerEvent(context);
memset(eventOut, 0, sizeof(*eventOut));
eventOut->Size = sizeof(*eventOut);
eventOut->Type = event.Type;
eventOut->Sequence = event.Sequence;
if (event.Type == SYS_INPUT_MANAGER_EVENT_KEY)
eventOut->Data.Key = event.Data.Key;
else if (event.Type == SYS_INPUT_MANAGER_EVENT_MOUSE)
eventOut->Data.Mouse = event.Data.Mouse;
else
return NTLSTATUS_FAILURE;
return NTLSTATUS_SUCCESS;
}
if (timeoutMs == 0) return NTLSTATUS_WOULD_BLOCK;
if (!context->ManagerWaitEvent) return NTLSTATUS_FAILURE;
EVENT* waitEvent = context->ManagerWaitEvent;
uint32_t selected = 0;
KSTATUS status = KeWaitForMultipleEvents(&waitEvent, 1, 0, timeoutMs, &selected);
if (!KSUCCESS(status)) return status;
}
}
// Implements the input Register Device operation.
__declspec(dllexport) KSTATUS KeInputRegisterDevice(const char* name, uint32_t flags, uint32_t* outDeviceId) {
(void)flags;
uint32_t id = 0;
KSTATUS status = KeAllocateId32(&gInputDeviceIds, &id);
if (!KSUCCESS(status)) return status;
if (outDeviceId) *outDeviceId = id;
DbgPrint("Input: registered device %u (%s)\n", id, name ? name : "unnamed");
return NTLSTATUS_SUCCESS;
}
// Implements the input Submit Key operation.
__declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (!event) return;
INPUT_KEY key = event->Key;
if (key == INPUT_KEY_NONE || key >= MAX_INPUT_KEYS) return;
KE_INPUT_MANAGER_EVENT managerEvent;
memset(&managerEvent, 0, sizeof(managerEvent));
managerEvent.Type = SYS_INPUT_MANAGER_EVENT_KEY;
managerEvent.Data.Key = *event;
KiInputQueueManagerEvent(context, &managerEvent);
bool wasDown = context->KeyDown[key] != 0;
bool pressed = event->Pressed;
if (pressed) {
// The input core owns typematic repeat. Treat repeated hardware make
// events as idempotent so PS/2 typematic and USB HID share one policy.
if (wasDown)
return;
context->KeyDown[key] = 1u;
KiInputSetModifier(context, key, true);
KiInputBumpGeneration(context);
KiInputQueueKey(context, key, KiInputKeyToChar(context, key));
KiInputStartRepeat(context, key, KiInputNowTicks(context));
} else {
if (!wasDown)
return;
context->KeyDown[key] = 0u;
KiInputSetModifier(context, key, false);
KiInputBumpGeneration(context);
KiInputStopRepeat(context, key);
}
}
// Implements the input Commit Mouse Position operation.
static bool KiInputCommitMousePosition(KE_INPUT_CONTEXT* context, int nx, int ny, uint8_t buttons) {
int bw = context->MouseBoundsWidth;
int bh = context->MouseBoundsHeight;
if (bw < 1) bw = 1;
if (bh < 1) bh = 1;
if (nx < 0) nx = 0;
if (ny < 0) ny = 0;
if (nx >= bw) nx = bw - 1;
if (ny >= bh) ny = bh - 1;
bool changed = (nx != context->MouseX) || (ny != context->MouseY) || (buttons != context->MouseButtons);
context->MouseX = nx;
context->MouseY = ny;
context->MouseButtons = buttons;
return changed;
}
// Implements the input Submit Mouse operation.
__declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (!event) return;
KE_INPUT_MANAGER_EVENT managerEvent;
memset(&managerEvent, 0, sizeof(managerEvent));
managerEvent.Type = SYS_INPUT_MANAGER_EVENT_MOUSE;
managerEvent.Data.Mouse = *event;
KiInputQueueManagerEvent(context, &managerEvent);
int oldX = context->MouseX;
int oldY = context->MouseY;
int nx = oldX + event->dx;
int ny = oldY + event->dy;
bool changed = KiInputCommitMousePosition(context, nx, ny, event->buttons);
if (event->wheel != 0) {
context->MouseWheel += event->wheel;
changed = true;
}
if (changed) KiInputBumpGeneration(context);
int next = (context->MouseTail + 1) & (int)MOUSE_QUEUE_MASK;
if (next != context->MouseHead) {
context->MouseQueue[context->MouseTail] = *event;
context->MouseTail = next;
}
}
// Implements the input Submit Mouse Absolute operation.
__declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE_EVENT* event) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (!event) return;
int oldX = context->MouseX;
int oldY = context->MouseY;
INPUT_MOUSE_EVENT managerMouse;
managerMouse.dx = event->x - oldX;
managerMouse.dy = event->y - oldY;
managerMouse.wheel = event->wheel;
managerMouse.buttons = event->buttons;
KE_INPUT_MANAGER_EVENT managerEvent;
memset(&managerEvent, 0, sizeof(managerEvent));
managerEvent.Type = SYS_INPUT_MANAGER_EVENT_MOUSE;
managerEvent.Data.Mouse = managerMouse;
KiInputQueueManagerEvent(context, &managerEvent);
bool changed = KiInputCommitMousePosition(context, event->x, event->y, event->buttons);
if (event->wheel != 0) {
context->MouseWheel += event->wheel;
changed = true;
}
if (changed) KiInputBumpGeneration(context);
INPUT_MOUSE_EVENT rel;
rel.dx = context->MouseX - oldX;
rel.dy = context->MouseY - oldY;
rel.wheel = event->wheel;
rel.buttons = event->buttons;
int next = (context->MouseTail + 1) & (int)MOUSE_QUEUE_MASK;
if (next != context->MouseHead) {
context->MouseQueue[context->MouseTail] = rel;
context->MouseTail = next;
}
}
// Implements the input Submit Mouse Absolute Scaled operation.
__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int wheel, uint8_t buttons) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
int bw = context->MouseBoundsWidth;
int bh = context->MouseBoundsHeight;
if (bw < 1) bw = 1;
if (bh < 1) bh = 1;
INPUT_MOUSE_ABSOLUTE_EVENT ev;
ev.x = maxX ? (int)(((uint64_t)rawX * (uint64_t)(bw - 1)) / (uint64_t)maxX) : 0;
ev.y = maxY ? (int)(((uint64_t)rawY * (uint64_t)(bh - 1)) / (uint64_t)maxY) : 0;
ev.wheel = wheel;
ev.buttons = buttons;
KeInputSubmitMouseAbsolute(&ev);
}
// Returns the next queued key without consuming it.
INPUT_KEY KeInputKbPoll(void) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (context->KeyHead == context->KeyTail) KiInputServiceRepeat(context);
return (context->KeyHead == context->KeyTail) ? INPUT_KEY_NONE : context->KeyQueue[context->KeyHead].Key;
}
// Waits for keyboard input in one context or until the timeout expires.
static KSTATUS KiInputWaitKey(KE_INPUT_CONTEXT* context, uint8_t alertable, uint64_t timeoutMs) {
if (!context)
return NTLSTATUS_INVALID_PARAMETER;
KiInputServiceRepeat(context);
if (context->KeyHead != context->KeyTail)
return NTLSTATUS_SUCCESS;
if (!context->WaitEvent)
return NTLSTATUS_FAILURE;
while (true) {
EVENT* event = context->WaitEvent;
uint32_t selected = 0;
KSTATUS s = KeWaitForMultipleEvents(&event, 1, alertable, timeoutMs, &selected);
if (!KSUCCESS(s))
return s;
KiInputServiceRepeat(context);
if (context->KeyHead != context->KeyTail)
return NTLSTATUS_SUCCESS;
}
}
// Waits for keyboard input in the default input context.
KSTATUS KeInputWaitKey(uint8_t alertable, uint64_t timeoutMs) {
return KiInputWaitKey(&gDefaultInputContext, alertable, timeoutMs);
}
// Services typematic repeat from the scheduler timer bottom half.
void KeInputTimerTick(void) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
INPUT_KEY key = context->RepeatKey;
if (!KiInputKeyIsRepeatable(key) || key >= MAX_INPUT_KEYS || !context->KeyDown[key])
return;
uint64_t now = KiInputNowTicks(context);
if (now != 0 && now >= context->RepeatNextTick)
KeInsertQueueDpc(&context->RepeatDpc, (void*)0, (void*)0);
}
// Implements the input Is Key Down operation.
bool KeInputIsKeyDown(INPUT_KEY key) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (key == INPUT_KEY_NONE || key >= MAX_INPUT_KEYS) return false;
return context->KeyDown[key] != 0;
}
// Peeks one keyboard event from an input context without consuming it.
static bool KiInputPeekEvent(KE_INPUT_CONTEXT* context, INPUT_KEY_RECORD* record) {
if (!context || !record) return false;
if (context->KeyHead == context->KeyTail) KiInputServiceRepeat(context);
if (context->KeyHead == context->KeyTail) return false;
INPUT_KEY_QUEUE_ENTRY entry = context->KeyQueue[context->KeyHead];
record->Key = entry.Key;
record->Char = entry.Character;
record->Modifiers = entry.Modifiers;
return true;
}
// Reads one keyboard event from an input context.
static bool KiInputReadEvent(KE_INPUT_CONTEXT* context, INPUT_KEY_RECORD* record) {
if (!KiInputPeekEvent(context, record)) return false;
context->KeyHead = (context->KeyHead + 1) & (int)KEY_QUEUE_MASK;
KiInputRefreshKeyboardEvent(context);
return true;
}
// Peeks one keyboard event from the default input context.
bool KeInputPeekEvent(INPUT_KEY_RECORD* record) {
return KiInputPeekEvent(&gDefaultInputContext, record);
}
// Reads one keyboard event from the default input context.
bool KeInputReadEvent(INPUT_KEY_RECORD* record) {
return KiInputReadEvent(&gDefaultInputContext, record);
}
// Implements the input Read Key operation.
INPUT_KEY KeInputReadKey(void) {
INPUT_KEY_RECORD record;
return KeInputReadEvent(&record) ? record.Key : INPUT_KEY_NONE;
}
// Implements the input Read Char operation.
char KeInputReadChar(void) {
INPUT_KEY_RECORD record;
return KeInputReadEvent(&record) ? record.Char : 0;
}
// Implements the input Get Modifiers operation.
uint32_t KeInputGetModifiers(void) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
return context->Modifiers;
}
// Snapshots the current physical global input state without consuming events.
KSTATUS KeInputQueryGlobalState(SYS_GLOBAL_INPUT_STATE* out) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (!out) return NTLSTATUS_INVALID_PARAMETER;
out->Size = sizeof(*out);
out->Flags = SYS_GLOBAL_INPUT_STATE_FLAG_NONE;
out->Generation = (uint64_t)context->Generation;
out->Modifiers = context->Modifiers;
memset(out->KeyDown, 0, sizeof(out->KeyDown));
for (uint32_t key = 0; key < MAX_INPUT_KEYS; key++) {
if (context->KeyDown[key])
out->KeyDown[key >> 3] |= (uint8_t)(1u << (key & 7u));
}
out->MouseX = context->MouseX;
out->MouseY = context->MouseY;
out->MouseWheel = context->MouseWheel;
out->MouseButtons = context->MouseButtons;
return NTLSTATUS_SUCCESS;
}
// Implements the input Mouse Peek operation.
bool KeInputMousePeek(INPUT_MOUSE_EVENT* event) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (!event) return false;
if (context->MouseHead == context->MouseTail) return false;
*event = context->MouseQueue[context->MouseHead];
return true;
}
// Implements the input Mouse Read operation.
bool KeInputMouseRead(INPUT_MOUSE_EVENT* event) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (!KeInputMousePeek(event)) return false;
context->MouseHead = (context->MouseHead + 1) & (int)MOUSE_QUEUE_MASK;
return true;
}
// Implements the input Mouse Get Position operation.
void KeInputMouseGetPosition(int* x, int* y) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (x) *x = context->MouseX;
if (y) *y = context->MouseY;
}
// Implements the input Mouse Set Position operation.
void KeInputMouseSetPosition(int x, int y) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
int bw = context->MouseBoundsWidth;
int bh = context->MouseBoundsHeight;
if (bw < 1) bw = 1;
if (bh < 1) bh = 1;
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x >= bw) x = bw - 1;
if (y >= bh) y = bh - 1;
bool changed = (x != context->MouseX) || (y != context->MouseY);
context->MouseX = x;
context->MouseY = y;
if (changed) KiInputBumpGeneration(context);
}
// Implements the input Mouse Set Bounds operation.
void KeInputMouseSetBounds(int w, int h) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (w < 1) w = 1;
if (h < 1) h = 1;
context->MouseBoundsWidth = w;
context->MouseBoundsHeight = h;
KeInputMouseSetPosition(context->MouseX, context->MouseY);
}
// Destroys an input queue handle.
static void KiInputQueueDestroy(OBJECT* object) {
INPUT_QUEUE* queue = CONTAINING_RECORD(object, INPUT_QUEUE, Header);
if (queue->WaitEvent) {
ObDereferenceObject(&queue->WaitEvent->Header);
queue->WaitEvent = (EVENT*)0;
}
queue->Context = (KE_INPUT_CONTEXT*)0;
}
// Opens the default keyboard input queue as a waitable object.
KSTATUS KeInputOpenQueue(INPUT_QUEUE** out) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
if (!out) return NTLSTATUS_INVALID_PARAMETER;
*out = (INPUT_QUEUE*)0;
if (!context->WaitEvent) return NTLSTATUS_FAILURE;
INPUT_QUEUE* queue = (INPUT_QUEUE*)ObAllocateObject(ObjectTypeInputQueue, sizeof(INPUT_QUEUE));
if (!queue) return NTLSTATUS_NO_MEMORY;
queue->Header.Destroy = KiInputQueueDestroy;
queue->Context = context;
ObReferenceObject(&context->WaitEvent->Header);
queue->WaitEvent = context->WaitEvent;
*out = queue;
return NTLSTATUS_SUCCESS;
}
// Peeks one keyboard event from the queue's owning context.
bool KeInputQueuePeekEvent(INPUT_QUEUE* queue, INPUT_KEY_RECORD* record) {
return queue ? KiInputPeekEvent(queue->Context, record) : false;
}
// Reads one keyboard event from the queue's owning context.
bool KeInputQueueReadEvent(INPUT_QUEUE* queue, INPUT_KEY_RECORD* record) {
return queue ? KiInputReadEvent(queue->Context, record) : false;
}
// Waits for keyboard input in the queue's owning context.
KSTATUS KeInputQueueWaitKey(INPUT_QUEUE* queue, uint8_t alertable, uint64_t timeoutMs) {
return queue ? KiInputWaitKey(queue->Context, alertable, timeoutMs)
: NTLSTATUS_INVALID_PARAMETER;
}
// Returns the event that wakes when keyboard input can be read.
EVENT* KeInputQueueWaitEvent(INPUT_QUEUE* queue) {
return queue ? queue->WaitEvent : (EVENT*)0;
}