// RSSRV logical input. The kernel reports normalized physical events; RSSRV owns interpretation, pointer state, and routing. #include #include "rssrv_internal.h" #include #include #include #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); }