603 lines
21 KiB
C
603 lines
21 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 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_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 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;
|
|
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->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);
|
|
}
|
|
KiInputRefreshKeyboardEvent(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);
|
|
}
|
|
|
|
// 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;
|
|
|
|
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;
|
|
KeDisplaySetCursorPosition(nx, ny);
|
|
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;
|
|
|
|
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;
|
|
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;
|
|
KeDisplaySetCursorPosition(x, 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;
|
|
}
|