Files

235 lines
9.8 KiB
C

// Native desktop client helpers. The protocol stays capability-based and does not expose DWM process identity.
#include <ntlite/desktop.h>
#include <ntlite/port.h>
#include <ntlite/resource.h>
#include <ntlite/syscall.h>
#include <string.h>
// Performs one fixed desktop request and validates the fixed reply envelope.
static KSTATUS NtlDesktopCall(SYS_HANDLE session,
NTL_DESKTOP_REQUEST* request,
const SYS_PORT_HANDLE_TRANSFER* transfer,
uint32_t transferCount,
NTL_DESKTOP_REPLY* replyOut) {
if (session == SYS_HANDLE_INVALID || !request || !replyOut || transferCount > 1u ||
(transferCount && !transfer))
return NTLSTATUS_INVALID_PARAMETER;
memset(replyOut, 0, sizeof(*replyOut));
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.RequestHandles = transfer;
call.RequestHandleCount = transferCount;
call.ReplyPayload = replyOut;
call.ReplyCapacity = sizeof(*replyOut);
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.HandleCount != 0 ||
info.PayloadLength != sizeof(*replyOut) || replyOut->Size != sizeof(*replyOut) ||
replyOut->Version != NTL_DESKTOP_PROTOCOL_VERSION ||
replyOut->Operation != request->Operation || replyOut->Flags != NTL_DESKTOP_FLAG_NONE)
return NTLSTATUS_FAILURE;
return replyOut->Status;
}
// Initializes one request envelope.
static void NtlDesktopInitRequest(NTL_DESKTOP_REQUEST* request, uint32_t operation, NTL_WINDOW_ID window) {
memset(request, 0, sizeof(*request));
request->Size = sizeof(*request);
request->Version = NTL_DESKTOP_PROTOCOL_VERSION;
request->Operation = operation;
request->Flags = NTL_DESKTOP_FLAG_NONE;
request->WindowId = window;
}
// Connects to the active Session 0 desktop endpoint through RSSRV.
KSTATUS NtlDesktopConnect(SYS_HANDLE* sessionOut) {
if (!sessionOut) return NTLSTATUS_INVALID_PARAMETER;
*sessionOut = SYS_HANDLE_INVALID;
SYS_HANDLE resource = SYS_HANDLE_INVALID;
SYS_HANDLE listener = SYS_HANDLE_INVALID;
KSTATUS status = NtlResourceConnect(&resource);
if (!KSUCCESS(status)) return status;
status = NtlResourceLookup(resource, SYS_SV_LITERAL(NTL_DESKTOP_SERVICE_PATH),
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT, &listener);
(void)NtlResourceClose(resource);
if (!KSUCCESS(status)) return status;
SYS_CONNECT_PORT_REQUEST connect;
memset(&connect, 0, sizeof(connect));
connect.Size = sizeof(connect);
connect.ListenerHandle = listener;
connect.Flags = SYS_PORT_CONNECT_FLAG_NONE;
connect.EndpointHandleOut = sessionOut;
status = SysConnectPort(&connect);
(void)SysCloseHandle(listener);
return status;
}
// Creates one hidden client-owned top-level window.
KSTATUS NtlDesktopCreateWindow(SYS_HANDLE session, const DISPLAY_RECT* bounds, NTL_WINDOW_ID* windowOut) {
if (!bounds || !windowOut) return NTLSTATUS_INVALID_PARAMETER;
*windowOut = 0;
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_CREATE_WINDOW, 0);
request.Bounds = *bounds;
NTL_DESKTOP_REPLY reply;
KSTATUS status = NtlDesktopCall(session, &request, 0, 0, &reply);
if (KSUCCESS(status)) *windowOut = reply.WindowId;
return status;
}
// Destroys one window owned by this connection.
KSTATUS NtlDesktopDestroyWindow(SYS_HANDLE session, NTL_WINDOW_ID window) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_DESTROY_WINDOW, window);
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Changes one window's screen-space bounds.
KSTATUS NtlDesktopSetBounds(SYS_HANDLE session, NTL_WINDOW_ID window, const DISPLAY_RECT* bounds) {
if (!bounds) return NTLSTATUS_INVALID_PARAMETER;
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_SET_BOUNDS, window);
request.Bounds = *bounds;
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Shows or hides one window without changing its z-order.
KSTATUS NtlDesktopSetVisible(SYS_HANDLE session, NTL_WINDOW_ID window, bool visible) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_SET_VISIBLE, window);
request.Value = visible ? 1u : 0u;
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Moves one window to the top of the z-order.
KSTATUS NtlDesktopRaiseWindow(SYS_HANDLE session, NTL_WINDOW_ID window) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_RAISE_WINDOW, window);
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Moves one window to the bottom of the z-order.
KSTATUS NtlDesktopLowerWindow(SYS_HANDLE session, NTL_WINDOW_ID window) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_LOWER_WINDOW, window);
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Requests Session 0 keyboard focus for one visible window.
KSTATUS NtlDesktopRequestFocus(SYS_HANDLE session, NTL_WINDOW_ID window) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_REQUEST_FOCUS, window);
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Requests Session 0 pointer capture for one visible window.
KSTATUS NtlDesktopRequestCapture(SYS_HANDLE session, NTL_WINDOW_ID window) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_REQUEST_CAPTURE, window);
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Releases pointer capture only when this window currently owns it.
KSTATUS NtlDesktopReleaseCapture(SYS_HANDLE session, NTL_WINDOW_ID window) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_RELEASE_CAPTURE, window);
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Sets visible cursor policy for the focused/captured window.
KSTATUS NtlDesktopSetPointerMode(SYS_HANDLE session, NTL_WINDOW_ID window, uint32_t mode) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_SET_POINTER_MODE, window);
request.Value = mode;
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Attaches a shared surface. DWM receives only QUERY, WAIT, and MAP_READ rights.
KSTATUS NtlDesktopAttachSurface(SYS_HANDLE session, NTL_WINDOW_ID window, SYS_HANDLE surface) {
if (surface == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER;
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_ATTACH_SURFACE, window);
SYS_PORT_HANDLE_TRANSFER transfer;
transfer.Handle = surface;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_SURFACE_RIGHT_MAP_READ;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, &transfer, 1u, &reply);
}
// Detaches the current shared surface from one window.
KSTATUS NtlDesktopDetachSurface(SYS_HANDLE session, NTL_WINDOW_ID window) {
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_DETACH_SURFACE, window);
NTL_DESKTOP_REPLY reply;
return NtlDesktopCall(session, &request, 0, 0, &reply);
}
// Queries one connection-owned window.
KSTATUS NtlDesktopQueryWindow(SYS_HANDLE session, NTL_WINDOW_ID window, NTL_DESKTOP_REPLY* replyOut) {
if (!replyOut) return NTLSTATUS_INVALID_PARAMETER;
NTL_DESKTOP_REQUEST request;
NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_QUERY_WINDOW, window);
return NtlDesktopCall(session, &request, 0, 0, replyOut);
}
// Receives one asynchronous key or mouse event from DWM.
KSTATUS NtlDesktopReceiveEvent(SYS_HANDLE session, uint64_t timeoutMilliseconds, NTL_DESKTOP_EVENT* eventOut) {
if (session == SYS_HANDLE_INVALID || !eventOut) return NTLSTATUS_INVALID_PARAMETER;
memset(eventOut, 0, sizeof(*eventOut));
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 = session;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = eventOut;
receive.PayloadCapacity = sizeof(*eventOut);
receive.InfoOut = &info;
receive.TimeoutMilliseconds = timeoutMilliseconds;
KSTATUS status = SysReceivePort(&receive);
if (!KSUCCESS(status)) return status;
if (info.Type != SYS_PORT_MESSAGE_NOTIFICATION || info.HandleCount != 0 ||
info.PayloadLength != sizeof(*eventOut) || eventOut->Size != sizeof(*eventOut) ||
eventOut->Version != NTL_DESKTOP_PROTOCOL_VERSION || eventOut->Flags != NTL_DESKTOP_FLAG_NONE ||
(eventOut->Type != NTL_DESKTOP_EVENT_KEY && eventOut->Type != NTL_DESKTOP_EVENT_MOUSE))
return NTLSTATUS_FAILURE;
return NTLSTATUS_SUCCESS;
}
// Disconnects and closes one desktop session endpoint.
KSTATUS NtlDesktopClose(SYS_HANDLE session) {
if (session == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
KSTATUS disconnect = SysDisconnectPort(session);
KSTATUS close = SysCloseHandle(session);
if (!KSUCCESS(disconnect) && disconnect != NTLSTATUS_PORT_DISCONNECTED)
return disconnect;
return close;
}