Files
nt-lite/code/user/system/rssrv/input.c
T

634 lines
26 KiB
C

// RSSRV logical input. The kernel reports normalized physical events; RSSRV owns interpretation, pointer state, and routing.
#include <stdbool.h>
#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)
#define RSSRV_INPUT_REPEAT_INITIAL_MS 400u
#define RSSRV_INPUT_REPEAT_PERIOD_MS 33u
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_ESCAPE, '\x1B', '\x1B' },
{ 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 bool 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 bool 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 false;
default:
return true;
}
}
// Arms RSSRV's logical typematic timer for one repeatable key.
static void RssrvInputStartRepeatLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key) {
if (!input || input->RepeatTimer == SYS_HANDLE_INVALID || !RssrvInputKeyIsRepeatable(key))
return;
input->RepeatKey = key;
SYS_SET_TIMER_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.TimerHandle = input->RepeatTimer;
request.Flags = SYS_TIMER_FLAG_NONE;
request.DueTimeMilliseconds = RSSRV_INPUT_REPEAT_INITIAL_MS;
request.PeriodMilliseconds = RSSRV_INPUT_REPEAT_PERIOD_MS;
(void)SysSetTimer(&request);
}
// Stops RSSRV typematic when the active repeat key is released.
static void RssrvInputStopRepeatLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key) {
if (!input || input->RepeatKey != key) return;
input->RepeatKey = INPUT_KEY_NONE;
if (input->RepeatTimer != SYS_HANDLE_INVALID)
(void)SysCancelTimer(input->RepeatTimer);
}
// 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;
RssrvInputStartRepeatLocked(input, key);
}
// Repeated hardware make events are intentionally ignored. RSSRV owns
// logical typematic so PS/2 and USB input share one repeat policy.
} else if (wasDown) {
RssrvInputSetKeyDownLocked(input, key, 0);
RssrvInputUpdateModifierLocked(input, key, 0);
RssrvInputStopRepeatLocked(input, key);
input->Generation++;
}
RssrvInputUnlock(input);
if (routeRecord) {
RssrvSessionObserveKey(input->Session, &routedRecord);
RssrvSessionRouteKey(input->Session, &routedRecord);
}
}
// Emits logical typematic records while the active repeat key remains down.
static void RssrvInputRepeatThread(uintptr_t argument) {
RSSRV_INPUT_STATE* input = (RSSRV_INPUT_STATE*)argument;
RssrvSetCurrentThreadName("rssrv.input.repeat");
if (!input || input->RepeatTimer == SYS_HANDLE_INVALID) SysExitThread(1);
for (;;) {
KSTATUS status = SysWait(input->RepeatTimer, SYS_WAIT_FLAG_NONE);
if (!KSUCCESS(status)) SysExitThread(2);
INPUT_KEY_RECORD routedRecord;
memset(&routedRecord, 0, sizeof(routedRecord));
int routeRecord = 0;
if (KSUCCESS(RssrvInputLock(input))) {
INPUT_KEY key = input->RepeatKey;
if (RssrvInputKeyIsRepeatable(key) && RssrvInputKeyDownLocked(input, key)) {
RssrvInputQueueKeyLocked(input, key, &routedRecord);
routeRecord = 1;
}
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;
int32_t x = 0;
int32_t y = 0;
bool changed = false;
if (!KSUCCESS(RssrvInputLock(input))) return;
int32_t nx = input->MouseX + event->dx;
int32_t ny = input->MouseY + event->dy;
int32_t width = input->MouseWidth > 0 ? input->MouseWidth : 1;
int32_t 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,
bool 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,
bool 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, false, 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, true, 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->RepeatTimer = SYS_HANDLE_INVALID;
input->Listener = SYS_HANDLE_INVALID;
input->RepeatKey = INPUT_KEY_NONE;
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 = SysCreateTimer(&input->RepeatTimer);
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)RssrvInputRepeatThread;
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);
}