Files

239 lines
8.7 KiB
C

// Kernel input core. This file owns canonical keyboard and mouse state shared by input drivers and syscalls.
#include <ntlite/atomic.h>
#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/ob_object.h>
#include <ntlite/syscall.h>
#define MANAGER_QUEUE_SIZE 256u
#define MANAGER_QUEUE_MASK (MANAGER_QUEUE_SIZE - 1u)
static KE_INPUT_CONTEXT gDefaultInputContext;
static KE_INPUT_MANAGER gInputManager;
static KE_ID_ALLOCATOR gInputDeviceIds = KE_ID_ALLOCATOR_INIT(1u);
// 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)NtlAtomicIncrement64(&context->ManagerSequence);
context->ManagerQueue[context->ManagerTail] = queued;
context->ManagerTail = next;
KiInputRefreshManagerEvent(context);
}
// Implements the input Initialize operation.
void KeInputInitialize(const HAL_API* hal) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
ObInitializeObjectHeader(&gInputManager.Header, ObjectTypeInputManager);
context->Hal = hal;
context->ManagerHead = context->ManagerTail = 0;
context->ManagerSequence = 0;
context->MouseBoundsWidth = 1920;
context->MouseBoundsHeight = 1080;
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);
}
KiInputRefreshManagerEvent(context);
int32_t 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 >= 256u) return;
// Forward physical transitions without interpreting key state or repeat policy.
KE_INPUT_MANAGER_EVENT managerEvent = {0};
managerEvent.Type = SYS_INPUT_MANAGER_EVENT_KEY;
managerEvent.Data.Key = *event;
KiInputQueueManagerEvent(context, &managerEvent);
}
// Implements the input Commit Mouse Position operation.
static void KiInputCommitMousePosition(KE_INPUT_CONTEXT* context, int32_t nx, int32_t ny) {
int32_t bw = context->MouseBoundsWidth;
int32_t 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;
context->MouseX = nx;
context->MouseY = ny;
}
// 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);
int32_t oldX = context->MouseX;
int32_t oldY = context->MouseY;
int32_t nx = oldX + event->dx;
int32_t ny = oldY + event->dy;
KiInputCommitMousePosition(context, nx, ny);
}
// 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;
int32_t oldX = context->MouseX;
int32_t 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);
KiInputCommitMousePosition(context, event->x, event->y);
}
// Implements the input Submit Mouse Absolute Scaled operation.
__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int32_t wheel, uint8_t buttons) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
int32_t bw = context->MouseBoundsWidth;
int32_t bh = context->MouseBoundsHeight;
if (bw < 1) bw = 1;
if (bh < 1) bh = 1;
INPUT_MOUSE_ABSOLUTE_EVENT ev;
ev.x = maxX ? (int32_t)(((uint64_t)rawX * (uint64_t)(bw - 1)) / (uint64_t)maxX) : 0;
ev.y = maxY ? (int32_t)(((uint64_t)rawY * (uint64_t)(bh - 1)) / (uint64_t)maxY) : 0;
ev.wheel = wheel;
ev.buttons = buttons;
KeInputSubmitMouseAbsolute(&ev);
}
// Implements the input Mouse Set Position operation.
void KeInputMouseSetPosition(int32_t x, int32_t y) {
KE_INPUT_CONTEXT* context = &gDefaultInputContext;
int32_t bw = context->MouseBoundsWidth;
int32_t 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;
context->MouseX = x;
context->MouseY = y;
}
// Implements the input Mouse Set Bounds operation.
void KeInputMouseSetBounds(int32_t w, int32_t 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);
}