Files

253 lines
9.6 KiB
C

// Native logical-input client. RSSRV owns keyboard interpretation and pointer policy; this file speaks its Port protocol.
#include <ntlite/atomic.h>
#include <stdbool.h>
#include <ntlite/input_service.h>
#include <ntlite/port.h>
#include <ntlite/resource.h>
#include <ntlite/syscall.h>
#include <intrin.h>
#include <string.h>
static volatile uint32_t gDefaultInputSession;
static KSTATUS NtlInputGetDefaultSession(SYS_HANDLE* sessionOut);
// Connects to the RSSRV listener published at /Services/Input.
KSTATUS NtlInputConnect(SYS_HANDLE* sessionOut) {
if (!sessionOut) return NTLSTATUS_INVALID_PARAMETER;
*sessionOut = SYS_HANDLE_INVALID;
SYS_HANDLE resourceSession = SYS_HANDLE_INVALID;
KSTATUS status = NtlResourceConnect(&resourceSession);
if (!KSUCCESS(status)) return status;
SYS_HANDLE listener = SYS_HANDLE_INVALID;
status = NtlResourceLookup(resourceSession,
SysSvFromCstr(NTL_INPUT_SERVICE_PATH),
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT,
&listener);
(void)NtlResourceClose(resourceSession);
if (!KSUCCESS(status)) return status;
status = NtlResourceConnectListener(listener, sessionOut);
(void)SysCloseHandle(listener);
return status;
}
// Disconnects one logical-input session.
KSTATUS NtlInputClose(SYS_HANDLE session) {
if (session == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER;
(void)SysDisconnectPort(session);
return SysCloseHandle(session);
}
// Executes one fixed input-service request and validates its reply envelope.
static KSTATUS NtlInputCall(SYS_HANDLE session,
uint32_t operation,
uint64_t timeoutMilliseconds,
NTL_INPUT_REPLY* replyOut) {
if (session == SYS_HANDLE_INVALID || !replyOut)
return NTLSTATUS_INVALID_PARAMETER;
NTL_INPUT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_INPUT_PROTOCOL_VERSION;
request.Operation = operation;
request.Flags = NTL_INPUT_FLAG_NONE;
request.TimeoutMilliseconds = timeoutMilliseconds;
NTL_INPUT_REPLY reply;
memset(&reply, 0, sizeof(reply));
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_CALL_PORT_REQUEST call;
memset(&call, 0, sizeof(call));
call.Size = sizeof(call);
call.EndpointHandle = session;
call.Flags = SYS_PORT_FLAG_NONE;
call.RequestPayload = &request;
call.RequestLength = sizeof(request);
call.ReplyPayload = &reply;
call.ReplyCapacity = sizeof(reply);
call.ReplyInfoOut = &info;
call.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysCallPort(&call);
if (!KSUCCESS(status)) return status;
if (info.Type != SYS_PORT_MESSAGE_REPLY || info.PayloadLength != sizeof(reply) || info.HandleCount != 0 ||
reply.Size != sizeof(reply) || reply.Version != NTL_INPUT_PROTOCOL_VERSION ||
reply.Operation != operation || reply.Flags != NTL_INPUT_FLAG_NONE)
return NTLSTATUS_FAILURE;
*replyOut = reply;
return reply.Status;
}
// Reads one logical keyboard event.
KSTATUS NtlInputReadEvent(SYS_HANDLE session, uint64_t timeoutMilliseconds, INPUT_KEY_RECORD* recordOut) {
if (!recordOut) return NTLSTATUS_INVALID_PARAMETER;
if (session == SYS_HANDLE_INVALID) {
KSTATUS connectStatus = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(connectStatus)) return connectStatus;
}
NTL_INPUT_REPLY reply;
KSTATUS status = NtlInputCall(session, NTL_INPUT_OP_READ_KEY, timeoutMilliseconds, &reply);
if (KSUCCESS(status)) *recordOut = reply.Key;
return status;
}
// Peeks one logical keyboard event.
KSTATUS NtlInputPeekEvent(SYS_HANDLE session, uint64_t timeoutMilliseconds, INPUT_KEY_RECORD* recordOut) {
if (!recordOut) return NTLSTATUS_INVALID_PARAMETER;
if (session == SYS_HANDLE_INVALID) {
KSTATUS connectStatus = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(connectStatus)) return connectStatus;
}
NTL_INPUT_REPLY reply;
KSTATUS status = NtlInputCall(session, NTL_INPUT_OP_PEEK_KEY, timeoutMilliseconds, &reply);
if (KSUCCESS(status)) *recordOut = reply.Key;
return status;
}
// Reads one logical mouse event.
KSTATUS NtlInputReadMouse(SYS_HANDLE session, uint64_t timeoutMilliseconds, INPUT_MOUSE_EVENT* eventOut) {
if (!eventOut) return NTLSTATUS_INVALID_PARAMETER;
if (session == SYS_HANDLE_INVALID) {
KSTATUS connectStatus = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(connectStatus)) return connectStatus;
}
NTL_INPUT_REPLY reply;
KSTATUS status = NtlInputCall(session, NTL_INPUT_OP_READ_MOUSE, timeoutMilliseconds, &reply);
if (KSUCCESS(status)) *eventOut = reply.Mouse;
return status;
}
// Peeks one logical mouse event.
KSTATUS NtlInputPeekMouse(SYS_HANDLE session, uint64_t timeoutMilliseconds, INPUT_MOUSE_EVENT* eventOut) {
if (!eventOut) return NTLSTATUS_INVALID_PARAMETER;
if (session == SYS_HANDLE_INVALID) {
KSTATUS connectStatus = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(connectStatus)) return connectStatus;
}
NTL_INPUT_REPLY reply;
KSTATUS status = NtlInputCall(session, NTL_INPUT_OP_PEEK_MOUSE, timeoutMilliseconds, &reply);
if (KSUCCESS(status)) *eventOut = reply.Mouse;
return status;
}
// Queries one RSSRV-owned logical input snapshot.
KSTATUS NtlInputQueryState(SYS_HANDLE session, NTL_INPUT_STATE* stateOut) {
if (!stateOut) return NTLSTATUS_INVALID_PARAMETER;
if (session == SYS_HANDLE_INVALID) {
KSTATUS connectStatus = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(connectStatus)) return connectStatus;
}
NTL_INPUT_REPLY reply;
KSTATUS status = NtlInputCall(session, NTL_INPUT_OP_QUERY_STATE, 0, &reply);
if (KSUCCESS(status)) *stateOut = reply.State;
return status;
}
// Returns one process-local cached input session, connecting it on first use.
static KSTATUS NtlInputGetDefaultSession(SYS_HANDLE* sessionOut) {
if (!sessionOut) return NTLSTATUS_INVALID_PARAMETER;
uint32_t current = gDefaultInputSession;
if (current != 0) {
*sessionOut = (SYS_HANDLE)current;
return NTLSTATUS_SUCCESS;
}
SYS_HANDLE created = SYS_HANDLE_INVALID;
KSTATUS status = NtlInputConnect(&created);
if (!KSUCCESS(status)) return status;
uint32_t previous = NtlAtomicCompareExchangeU32(&gDefaultInputSession, created, 0);
if (previous != 0) {
(void)NtlInputClose(created);
created = (SYS_HANDLE)previous;
}
*sessionOut = created;
return NTLSTATUS_SUCCESS;
}
// Peeks the next logical key without consuming it.
KSTATUS NtlInputPollKeyboard(INPUT_KEY* keyOut) {
if (!keyOut) return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE session = SYS_HANDLE_INVALID;
KSTATUS status = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(status)) return status;
INPUT_KEY_RECORD record;
status = NtlInputPeekEvent(session, 0, &record);
if (KSUCCESS(status)) *keyOut = record.Key;
return status;
}
// Queries one logical key's current down state.
KSTATUS NtlInputIsKeyDown(INPUT_KEY key, bool* downOut) {
if (!downOut || key == INPUT_KEY_NONE || key >= NTL_INPUT_KEY_BYTES * 8u)
return NTLSTATUS_INVALID_PARAMETER;
NTL_INPUT_STATE state;
KSTATUS status = NtlInputQueryDefaultState(&state);
if (!KSUCCESS(status)) return status;
*downOut = (state.KeyDown[key >> 3] & (uint8_t)(1u << (key & 7u))) != 0;
return NTLSTATUS_SUCCESS;
}
// Consumes logical key records until one has a character representation.
KSTATUS NtlInputReadKeyboardChar(char* charOut) {
if (!charOut) return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE session = SYS_HANDLE_INVALID;
KSTATUS status = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(status)) return status;
for (;;) {
INPUT_KEY_RECORD record;
status = NtlInputReadEvent(session, SYS_PORT_TIMEOUT_INFINITE, &record);
if (!KSUCCESS(status)) return status;
if (record.Char != 0) {
*charOut = record.Char;
return NTLSTATUS_SUCCESS;
}
}
}
// Reads one logical key.
KSTATUS NtlInputReadKey(INPUT_KEY* keyOut) {
if (!keyOut) return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE session = SYS_HANDLE_INVALID;
KSTATUS status = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(status)) return status;
INPUT_KEY_RECORD record;
status = NtlInputReadEvent(session, SYS_PORT_TIMEOUT_INFINITE, &record);
if (KSUCCESS(status)) *keyOut = record.Key;
return status;
}
// Returns the current logical modifier mask, or zero if the service cannot be queried.
uint32_t NtlInputGetKeyModifiers(void) {
NTL_INPUT_STATE state;
return KSUCCESS(NtlInputQueryDefaultState(&state)) ? state.Modifiers : 0u;
}
// Queries the process-default logical input session.
KSTATUS NtlInputQueryDefaultState(NTL_INPUT_STATE* stateOut) {
if (!stateOut) return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE session = SYS_HANDLE_INVALID;
KSTATUS status = NtlInputGetDefaultSession(&session);
if (!KSUCCESS(status)) return status;
return NtlInputQueryState(session, stateOut);
}
// Returns the current RSSRV-owned logical pointer position.
KSTATUS NtlInputGetMousePos(int32_t* xOut, int32_t* yOut) {
if (!xOut || !yOut) return NTLSTATUS_INVALID_PARAMETER;
NTL_INPUT_STATE state;
KSTATUS status = NtlInputQueryDefaultState(&state);
if (!KSUCCESS(status)) return status;
*xOut = state.MouseX;
*yOut = state.MouseY;
return NTLSTATUS_SUCCESS;
}