565 lines
23 KiB
C
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);
|
|
}
|