239 lines
8.7 KiB
C
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);
|
|
}
|