Files
nt-lite/code/user/system/rssrv/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

565 lines
23 KiB
C

// RSSRV logical input. The kernel reports normalized physical events; RSSRV owns interpretation, pointer state, and routing.
#include "rssrv_internal.h"
#include <ntlite/port.h>
#include <stdlib.h>
#include <string.h>
#define RSSRV_EVENT_MANUAL_RESET 1u
#define RSSRV_INPUT_KEY_QUEUE_MASK (RSSRV_INPUT_KEY_QUEUE_SIZE - 1u)
#define RSSRV_INPUT_MOUSE_QUEUE_MASK (RSSRV_INPUT_MOUSE_QUEUE_SIZE - 1u)
typedef struct RSSRV_INPUT_SESSION {
RSSRV_INPUT_STATE* Input;
SYS_HANDLE Endpoint;
} RSSRV_INPUT_SESSION;
typedef struct RSSRV_KEY_CHAR_PAIR {
INPUT_KEY Key;
char Normal;
char Shifted;
} RSSRV_KEY_CHAR_PAIR;
static const RSSRV_KEY_CHAR_PAIR gInputCharMap[] = {
{ 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, '.', '.' },
};
// Acquires the input-state mutex.
static KSTATUS RssrvInputLock(RSSRV_INPUT_STATE* input) {
return input ? SysWait(input->Mutex, SYS_WAIT_FLAG_NONE) : NTLSTATUS_INVALID_PARAMETER;
}
// Releases the input-state mutex.
static void RssrvInputUnlock(RSSRV_INPUT_STATE* input) {
if (input) (void)SysReleaseMutex(input->Mutex);
}
// Tests one logical key bit while the input mutex is held.
static int RssrvInputKeyDownLocked(const RSSRV_INPUT_STATE* input, INPUT_KEY key) {
return key < NTL_INPUT_KEY_BYTES * 8u &&
(input->KeyDown[key >> 3] & (uint8_t)(1u << (key & 7u))) != 0;
}
// Updates one logical key bit while the input mutex is held.
static void RssrvInputSetKeyDownLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key, int down) {
uint8_t mask = (uint8_t)(1u << (key & 7u));
if (down) input->KeyDown[key >> 3] |= mask;
else input->KeyDown[key >> 3] &= (uint8_t)~mask;
}
// Returns whether duplicate physical make events should generate repeated logical key records.
static int RssrvInputKeyIsRepeatable(INPUT_KEY key) {
switch (key) {
case INPUT_KEY_NONE:
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 0;
default:
return 1;
}
}
// Applies modifier and lock-key state for one first make or final break.
static void RssrvInputUpdateModifierLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key, int 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) input->Modifiers |= bit;
else input->Modifiers &= ~bit;
}
if (pressed && key == INPUT_KEY_CAPS_LOCK) input->Modifiers ^= INPUT_MOD_CAPS_LOCK;
if (pressed && key == INPUT_KEY_NUM_LOCK) input->Modifiers ^= INPUT_MOD_NUM_LOCK;
}
// Maps one key through RSSRV's initial US keyboard layout.
static char RssrvInputKeyToCharLocked(const RSSRV_INPUT_STATE* input, INPUT_KEY key) {
if (key >= INPUT_KEY_A && key <= INPUT_KEY_Z) {
int shifted = (input->Modifiers & INPUT_MOD_SHIFT) != 0;
int caps = (input->Modifiers & INPUT_MOD_CAPS_LOCK) != 0;
char c = (char)('a' + (key - INPUT_KEY_A));
if (shifted ^ caps) c = (char)(c - 'a' + 'A');
return c;
}
int shifted = (input->Modifiers & INPUT_MOD_SHIFT) != 0;
for (size_t i = 0; i < sizeof(gInputCharMap) / sizeof(gInputCharMap[0]); ++i) {
if (gInputCharMap[i].Key == key)
return shifted ? gInputCharMap[i].Shifted : gInputCharMap[i].Normal;
}
return 0;
}
// Refreshes the keyboard availability event while the input mutex is held.
static void RssrvInputRefreshKeyEventLocked(RSSRV_INPUT_STATE* input) {
if (input->KeyHead != input->KeyTail) (void)SysSignalEvent(input->KeyEvent);
else (void)SysClearEvent(input->KeyEvent);
}
// Refreshes the mouse availability event while the input mutex is held.
static void RssrvInputRefreshMouseEventLocked(RSSRV_INPUT_STATE* input) {
if (input->MouseHead != input->MouseTail) (void)SysSignalEvent(input->MouseEvent);
else (void)SysClearEvent(input->MouseEvent);
}
// Builds one logical key record and mirrors it into the raw logical-input queue.
static void RssrvInputQueueKeyLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key, INPUT_KEY_RECORD* recordOut) {
INPUT_KEY_RECORD record;
memset(&record, 0, sizeof(record));
record.Key = key;
record.Char = RssrvInputKeyToCharLocked(input, key);
record.Modifiers = input->Modifiers;
if (recordOut) *recordOut = record;
uint32_t next = (input->KeyTail + 1u) & RSSRV_INPUT_KEY_QUEUE_MASK;
if (next == input->KeyHead) return;
input->KeyQueue[input->KeyTail] = record;
input->KeyTail = next;
RssrvInputRefreshKeyEventLocked(input);
}
// Applies one normalized physical keyboard make/break event and routes logical presses after releasing the input lock.
static void RssrvInputApplyKey(RSSRV_INPUT_STATE* input, const INPUT_KEY_EVENT* event) {
if (!input || !event || event->Key == INPUT_KEY_NONE || event->Key >= NTL_INPUT_KEY_BYTES * 8u)
return;
if (!KSUCCESS(RssrvInputLock(input))) return;
INPUT_KEY_RECORD routedRecord;
memset(&routedRecord, 0, sizeof(routedRecord));
int routeRecord = 0;
INPUT_KEY key = event->Key;
int wasDown = RssrvInputKeyDownLocked(input, key);
if (event->Pressed) {
if (!wasDown) {
RssrvInputSetKeyDownLocked(input, key, 1);
RssrvInputUpdateModifierLocked(input, key, 1);
input->Generation++;
RssrvInputQueueKeyLocked(input, key, &routedRecord);
routeRecord = 1;
} else if (RssrvInputKeyIsRepeatable(key)) {
// Hardware typematic make events remain useful until RSSRV grows its own repeat timer.
RssrvInputQueueKeyLocked(input, key, &routedRecord);
routeRecord = 1;
}
} else if (wasDown) {
RssrvInputSetKeyDownLocked(input, key, 0);
RssrvInputUpdateModifierLocked(input, key, 0);
input->Generation++;
}
RssrvInputUnlock(input);
if (routeRecord) {
RssrvSessionObserveKey(input->Session, &routedRecord);
RssrvSessionRouteKey(input->Session, &routedRecord);
}
}
// Applies one normalized relative mouse event and lets Session 0 own cursor and target routing policy.
static void RssrvInputApplyMouse(RSSRV_INPUT_STATE* input, const INPUT_MOUSE_EVENT* event) {
if (!input || !event) return;
int x = 0;
int y = 0;
int changed = 0;
if (!KSUCCESS(RssrvInputLock(input))) return;
int nx = input->MouseX + event->dx;
int ny = input->MouseY + event->dy;
int width = input->MouseWidth > 0 ? input->MouseWidth : 1;
int height = input->MouseHeight > 0 ? input->MouseHeight : 1;
if (nx < 0) nx = 0;
if (ny < 0) ny = 0;
if (nx >= width) nx = width - 1;
if (ny >= height) ny = height - 1;
changed = nx != input->MouseX || ny != input->MouseY ||
event->wheel != 0 || event->buttons != input->MouseButtons;
input->MouseX = nx;
input->MouseY = ny;
input->MouseWheel += event->wheel;
input->MouseButtons = event->buttons;
if (changed) input->Generation++;
uint32_t next = (input->MouseTail + 1u) & RSSRV_INPUT_MOUSE_QUEUE_MASK;
if (next != input->MouseHead) {
input->MouseQueue[input->MouseTail] = *event;
input->MouseTail = next;
RssrvInputRefreshMouseEventLocked(input);
}
x = input->MouseX;
y = input->MouseY;
RssrvInputUnlock(input);
if (changed) {
RssrvSessionObserveMouse(input->Session, event, x, y);
RssrvSessionRouteMouse(input->Session, event, x, y);
}
}
// Consumes the kernel's normalized physical input stream for the lifetime of RSSRV.
static void RssrvInputRawThread(uintptr_t argument) {
RSSRV_INPUT_STATE* input = (RSSRV_INPUT_STATE*)argument;
RssrvSetCurrentThreadName("rssrv.input.raw");
if (!input) SysExitThread(1);
for (;;) {
SYS_INPUT_MANAGER_EVENT event;
memset(&event, 0, sizeof(event));
SYS_INPUT_MANAGER_READ_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ManagerHandle = input->Manager;
request.Flags = SYS_INPUT_MANAGER_EVENT_FLAG_NONE;
request.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
request.EventOut = &event;
KSTATUS status = SysReadInputManagerEvent(&request);
if (!KSUCCESS(status)) RssrvExit(14);
if (event.Type == SYS_INPUT_MANAGER_EVENT_KEY)
RssrvInputApplyKey(input, &event.Data.Key);
else if (event.Type == SYS_INPUT_MANAGER_EVENT_MOUSE)
RssrvInputApplyMouse(input, &event.Data.Mouse);
}
}
// Reads or peeks one logical keyboard record, blocking on the queue event when requested.
KSTATUS RssrvInputReadKeyRecord(RSSRV_INPUT_STATE* input,
int peek,
uint64_t timeoutMilliseconds,
INPUT_KEY_RECORD* recordOut) {
if (!input || !recordOut) return NTLSTATUS_INVALID_PARAMETER;
for (;;) {
KSTATUS status = RssrvInputLock(input);
if (!KSUCCESS(status)) return status;
if (input->KeyHead != input->KeyTail) {
*recordOut = input->KeyQueue[input->KeyHead];
if (!peek) {
input->KeyHead = (input->KeyHead + 1u) & RSSRV_INPUT_KEY_QUEUE_MASK;
RssrvInputRefreshKeyEventLocked(input);
}
RssrvInputUnlock(input);
return NTLSTATUS_SUCCESS;
}
RssrvInputUnlock(input);
if (timeoutMilliseconds == 0) return NTLSTATUS_WOULD_BLOCK;
status = SysWaitSingle(input->KeyEvent, SYS_WAIT_FLAG_NONE, timeoutMilliseconds);
if (!KSUCCESS(status)) return status;
}
}
// Discards all queued logical keyboard records while preserving current key state.
KSTATUS RssrvInputFlushKeys(RSSRV_INPUT_STATE* input, uint32_t* discardedOut) {
if (!input) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvInputLock(input);
if (!KSUCCESS(status)) return status;
uint32_t discarded = (input->KeyTail - input->KeyHead) & RSSRV_INPUT_KEY_QUEUE_MASK;
input->KeyHead = input->KeyTail;
RssrvInputRefreshKeyEventLocked(input);
RssrvInputUnlock(input);
if (discardedOut) *discardedOut = discarded;
return NTLSTATUS_SUCCESS;
}
// Reads or peeks one logical mouse record, blocking on the queue event when requested.
static KSTATUS RssrvInputGetMouse(RSSRV_INPUT_STATE* input,
int peek,
uint64_t timeoutMilliseconds,
INPUT_MOUSE_EVENT* eventOut) {
if (!input || !eventOut) return NTLSTATUS_INVALID_PARAMETER;
for (;;) {
KSTATUS status = RssrvInputLock(input);
if (!KSUCCESS(status)) return status;
if (input->MouseHead != input->MouseTail) {
*eventOut = input->MouseQueue[input->MouseHead];
if (!peek) {
input->MouseHead = (input->MouseHead + 1u) & RSSRV_INPUT_MOUSE_QUEUE_MASK;
RssrvInputRefreshMouseEventLocked(input);
}
RssrvInputUnlock(input);
return NTLSTATUS_SUCCESS;
}
RssrvInputUnlock(input);
if (timeoutMilliseconds == 0) return NTLSTATUS_WOULD_BLOCK;
status = SysWaitSingle(input->MouseEvent, SYS_WAIT_FLAG_NONE, timeoutMilliseconds);
if (!KSUCCESS(status)) return status;
}
}
// Snapshots RSSRV's logical state while holding its state mutex.
static KSTATUS RssrvInputQueryState(RSSRV_INPUT_STATE* input, NTL_INPUT_STATE* stateOut) {
if (!input || !stateOut) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvInputLock(input);
if (!KSUCCESS(status)) return status;
memset(stateOut, 0, sizeof(*stateOut));
stateOut->Size = sizeof(*stateOut);
stateOut->Flags = NTL_INPUT_STATE_FLAG_NONE;
stateOut->Generation = input->Generation;
stateOut->Modifiers = input->Modifiers;
memcpy(stateOut->KeyDown, input->KeyDown, sizeof(stateOut->KeyDown));
stateOut->MouseX = input->MouseX;
stateOut->MouseY = input->MouseY;
stateOut->MouseWheel = input->MouseWheel;
stateOut->MouseButtons = input->MouseButtons;
RssrvInputUnlock(input);
return NTLSTATUS_SUCCESS;
}
// Sends one fixed logical-input reply.
static KSTATUS RssrvInputReply(SYS_HANDLE endpoint,
uint32_t messageId,
uint32_t operation,
KSTATUS status,
const INPUT_KEY_RECORD* key,
const INPUT_MOUSE_EVENT* mouse,
const NTL_INPUT_STATE* state) {
NTL_INPUT_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_INPUT_PROTOCOL_VERSION;
reply.Operation = operation;
reply.Flags = NTL_INPUT_FLAG_NONE;
reply.Status = status;
if (key) reply.Key = *key;
if (mouse) reply.Mouse = *mouse;
if (state) reply.State = *state;
SYS_REPLY_PORT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.EndpointHandle = endpoint;
request.MessageId = messageId;
request.Flags = SYS_PORT_FLAG_NONE;
request.Payload = &reply;
request.PayloadLength = sizeof(reply);
return SysReplyPort(&request);
}
// Serves one connected logical-input client.
static void RssrvInputSessionThread(uintptr_t argument) {
RSSRV_INPUT_SESSION* session = (RSSRV_INPUT_SESSION*)argument;
if (!session || !session->Input) {
if (session) free(session);
SysExitThread(1);
}
RSSRV_INPUT_STATE* input = session->Input;
SYS_HANDLE endpoint = session->Endpoint;
free(session);
RssrvSetCurrentThreadName("rssrv.input.session");
for (;;) {
NTL_INPUT_REQUEST request;
memset(&request, 0, sizeof(request));
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = endpoint;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = &request;
receive.PayloadCapacity = sizeof(request);
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED) break;
if (!KSUCCESS(status)) break;
if (info.Type != SYS_PORT_MESSAGE_REQUEST || info.PayloadLength != sizeof(request) || info.HandleCount != 0 ||
request.Size != sizeof(request) || request.Version != NTL_INPUT_PROTOCOL_VERSION ||
request.Flags != NTL_INPUT_FLAG_NONE) {
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation,
NTLSTATUS_INVALID_PARAMETER, 0, 0, 0);
continue;
}
INPUT_KEY_RECORD key;
INPUT_MOUSE_EVENT mouse;
NTL_INPUT_STATE state;
memset(&key, 0, sizeof(key));
memset(&mouse, 0, sizeof(mouse));
memset(&state, 0, sizeof(state));
switch (request.Operation) {
case NTL_INPUT_OP_READ_KEY:
status = RssrvInputReadKeyRecord(input, 0, request.TimeoutMilliseconds, &key);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
KSUCCESS(status) ? &key : 0, 0, 0);
break;
case NTL_INPUT_OP_PEEK_KEY:
status = RssrvInputReadKeyRecord(input, 1, request.TimeoutMilliseconds, &key);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
KSUCCESS(status) ? &key : 0, 0, 0);
break;
case NTL_INPUT_OP_READ_MOUSE:
status = RssrvInputGetMouse(input, 0, request.TimeoutMilliseconds, &mouse);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
0, KSUCCESS(status) ? &mouse : 0, 0);
break;
case NTL_INPUT_OP_PEEK_MOUSE:
status = RssrvInputGetMouse(input, 1, request.TimeoutMilliseconds, &mouse);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
0, KSUCCESS(status) ? &mouse : 0, 0);
break;
case NTL_INPUT_OP_QUERY_STATE:
status = RssrvInputQueryState(input, &state);
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status,
0, 0, KSUCCESS(status) ? &state : 0);
break;
default:
(void)RssrvInputReply(endpoint, info.MessageId, request.Operation,
NTLSTATUS_INVALID_PARAMETER, 0, 0, 0);
break;
}
}
(void)SysCloseHandle(endpoint);
SysExitThread(0);
}
// Accepts logical-input sessions and gives each connection one worker thread.
static void RssrvInputListenerThread(uintptr_t argument) {
RSSRV_INPUT_STATE* input = (RSSRV_INPUT_STATE*)argument;
RssrvSetCurrentThreadName("rssrv.input.listen");
if (!input) SysExitThread(1);
for (;;) {
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = input->Listener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = &endpoint;
KSTATUS status = SysAcceptPort(&accept);
if (!KSUCCESS(status)) RssrvExit(15);
RSSRV_INPUT_SESSION* session = (RSSRV_INPUT_SESSION*)malloc(sizeof(*session));
if (!session) {
(void)SysCloseHandle(endpoint);
continue;
}
session->Input = input;
session->Endpoint = endpoint;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvInputSessionThread;
create.Argument = (uintptr_t)session;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
KSTATUS createStatus = SysCreateThread(&create);
if (!KSUCCESS(createStatus)) {
free(session);
(void)SysCloseHandle(endpoint);
}
}
}
// Initializes RSSRV logical input and its initial display-sized pointer state.
KSTATUS RssrvInputInitialize(RSSRV_INPUT_STATE* input,
RSSRV_NAMESPACE* namespaceState,
RSSRV_SESSION_STATE* session,
SYS_HANDLE manager) {
if (!input || !namespaceState || !session || manager == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
memset(input, 0, sizeof(*input));
input->Manager = manager;
input->Namespace = namespaceState;
input->Session = session;
input->Mutex = SYS_HANDLE_INVALID;
input->KeyEvent = SYS_HANDLE_INVALID;
input->MouseEvent = SYS_HANDLE_INVALID;
input->Listener = SYS_HANDLE_INVALID;
KSTATUS status = SysCreateMutex(&input->Mutex);
if (!KSUCCESS(status)) return status;
status = SysCreateEvent(RSSRV_EVENT_MANUAL_RESET, 0, &input->KeyEvent);
if (!KSUCCESS(status)) return status;
status = SysCreateEvent(RSSRV_EVENT_MANUAL_RESET, 0, &input->MouseEvent);
if (!KSUCCESS(status)) return status;
status = RssrvSessionQueryDisplayBounds(session, &input->MouseWidth, &input->MouseHeight);
if (!KSUCCESS(status)) return status;
input->MouseX = input->MouseWidth / 2;
input->MouseY = input->MouseHeight / 2;
return NTLSTATUS_SUCCESS;
}
// Publishes /Services/Input and starts the raw-input and client-listener workers.
KSTATUS RssrvInputStart(RSSRV_INPUT_STATE* input) {
if (!input || !input->Namespace || input->Listener != SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
SYS_CREATE_PORT_REQUEST createPort;
memset(&createPort, 0, sizeof(createPort));
createPort.Size = sizeof(createPort);
createPort.Flags = SYS_PORT_FLAG_NONE;
createPort.Backlog = RSSRV_LISTENER_BACKLOG;
createPort.HandleOut = &input->Listener;
KSTATUS status = SysCreatePort(&createPort);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceBindOwned(input->Namespace,
NTL_INPUT_SERVICE_PATH,
sizeof(NTL_INPUT_SERVICE_PATH) - 1u,
NTL_RESOURCE_KIND_CAPABILITY,
input->Listener,
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
if (!KSUCCESS(status)) return status;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvInputRawThread;
create.Argument = (uintptr_t)input;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
status = SysCreateThread(&create);
if (!KSUCCESS(status)) return status;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvInputListenerThread;
create.Argument = (uintptr_t)input;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
return SysCreateThread(&create);
}