235 lines
9.8 KiB
C
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;
|
|
}
|