mirror of
https://github.com/hyblocker/OpenVR-SpaceCalibrator.git
synced 2026-10-07 11:00:20 +02:00
221 lines
5.2 KiB
C++
221 lines
5.2 KiB
C++
#include "IPCServer.h"
|
|
#include "Logging.h"
|
|
#include "ServerTrackedDeviceProvider.h"
|
|
|
|
void IPCServer::HandleRequest(const protocol::Request &request, protocol::Response &response)
|
|
{
|
|
switch (request.type)
|
|
{
|
|
case protocol::RequestHandshake:
|
|
response.type = protocol::ResponseHandshake;
|
|
response.protocol.version = protocol::Version;
|
|
break;
|
|
|
|
case protocol::RequestSetDeviceTransform:
|
|
driver->SetDeviceTransform(request.setDeviceTransform);
|
|
response.type = protocol::ResponseSuccess;
|
|
break;
|
|
|
|
default:
|
|
LOG("Invalid IPC request: %d", request.type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
IPCServer::~IPCServer()
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
void IPCServer::Run()
|
|
{
|
|
mainThread = std::thread(RunThread, this);
|
|
}
|
|
|
|
void IPCServer::Stop()
|
|
{
|
|
TRACE("IPCServer::Stop()");
|
|
if (!running)
|
|
return;
|
|
|
|
stop = true;
|
|
SetEvent(connectEvent);
|
|
mainThread.join();
|
|
running = false;
|
|
TRACE("IPCServer::Stop() finished");
|
|
}
|
|
|
|
IPCServer::PipeInstance *IPCServer::CreatePipeInstance(HANDLE pipe)
|
|
{
|
|
auto pipeInst = new PipeInstance;
|
|
pipeInst->pipe = pipe;
|
|
pipeInst->server = this;
|
|
pipes.insert(pipeInst);
|
|
return pipeInst;
|
|
}
|
|
|
|
void IPCServer::ClosePipeInstance(PipeInstance *pipeInst)
|
|
{
|
|
DisconnectNamedPipe(pipeInst->pipe);
|
|
CloseHandle(pipeInst->pipe);
|
|
pipes.erase(pipeInst);
|
|
delete pipeInst;
|
|
}
|
|
|
|
void IPCServer::RunThread(IPCServer *_this)
|
|
{
|
|
_this->running = true;
|
|
LPTSTR pipeName = TEXT(OPENVR_SPACECALIBRATOR_PIPE_NAME);
|
|
|
|
HANDLE connectEvent = _this->connectEvent = CreateEvent(0, TRUE, TRUE, 0);
|
|
if (!connectEvent)
|
|
{
|
|
LOG("CreateEvent failed in RunThread. Error: %d", GetLastError());
|
|
return;
|
|
}
|
|
|
|
OVERLAPPED connectOverlap;
|
|
connectOverlap.hEvent = connectEvent;
|
|
|
|
HANDLE nextPipe;
|
|
BOOL connectPending = CreateAndConnectInstance(&connectOverlap, nextPipe);
|
|
|
|
while (!_this->stop)
|
|
{
|
|
DWORD wait = WaitForSingleObjectEx(connectEvent, INFINITE, TRUE);
|
|
|
|
if (_this->stop)
|
|
{
|
|
break;
|
|
}
|
|
else if (wait == 0)
|
|
{
|
|
// When connectPending is false, the last call to CreateAndConnectInstance
|
|
// picked up a connected client and triggered this event, so we can simply
|
|
// create a new pipe instance for it. If true, the client was still pending
|
|
// connection when CreateAndConnectInstance returned, so this event was triggered
|
|
// internally and we need to flush out the result, or something like that.
|
|
if (connectPending)
|
|
{
|
|
DWORD bytesConnect;
|
|
BOOL success = GetOverlappedResult(nextPipe, &connectOverlap, &bytesConnect, FALSE);
|
|
if (!success)
|
|
{
|
|
LOG("GetOverlappedResult failed in RunThread. Error: %d", GetLastError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG("IPC client connected");
|
|
|
|
auto pipeInst = _this->CreatePipeInstance(nextPipe);
|
|
CompletedWriteCallback(0, sizeof protocol::Response, (LPOVERLAPPED) pipeInst);
|
|
|
|
connectPending = CreateAndConnectInstance(&connectOverlap, nextPipe);
|
|
}
|
|
else if (wait != WAIT_IO_COMPLETION)
|
|
{
|
|
printf("WaitForSingleObjectEx failed in RunThread. Error %d", GetLastError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (auto &pipeInst : _this->pipes)
|
|
{
|
|
_this->ClosePipeInstance(pipeInst);
|
|
}
|
|
_this->pipes.clear();
|
|
}
|
|
|
|
BOOL IPCServer::CreateAndConnectInstance(LPOVERLAPPED overlap, HANDLE &pipe)
|
|
{
|
|
pipe = CreateNamedPipe(
|
|
TEXT(OPENVR_SPACECALIBRATOR_PIPE_NAME),
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
|
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
sizeof protocol::Request,
|
|
sizeof protocol::Response,
|
|
1000,
|
|
0
|
|
);
|
|
|
|
if (pipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
LOG("CreateNamedPipe failed. Error: %d", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
ConnectNamedPipe(pipe, overlap);
|
|
|
|
switch(GetLastError())
|
|
{
|
|
case ERROR_IO_PENDING:
|
|
// Mark a pending connection by returning true, and when the connection
|
|
// completes an event will trigger automatically.
|
|
return TRUE;
|
|
|
|
case ERROR_PIPE_CONNECTED:
|
|
// Signal the event loop that a client is connected.
|
|
if (SetEvent(overlap->hEvent))
|
|
return FALSE;
|
|
}
|
|
|
|
LOG("ConnectNamedPipe failed. Error: %d", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
void IPCServer::CompletedReadCallback(DWORD err, DWORD bytesRead, LPOVERLAPPED overlap)
|
|
{
|
|
PipeInstance *pipeInst = (PipeInstance *) overlap;
|
|
BOOL success = FALSE;
|
|
|
|
if (err == 0 && bytesRead > 0)
|
|
{
|
|
pipeInst->server->HandleRequest(pipeInst->request, pipeInst->response);
|
|
success = WriteFileEx(
|
|
pipeInst->pipe,
|
|
&pipeInst->response,
|
|
sizeof protocol::Response,
|
|
overlap,
|
|
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteCallback
|
|
);
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
if (err == ERROR_BROKEN_PIPE)
|
|
{
|
|
LOG("IPC client disconnecting normally");
|
|
}
|
|
else
|
|
{
|
|
LOG("IPC client disconnecting due to error (via CompletedReadCallback), error: %d, bytesRead: %d", err, bytesRead);
|
|
}
|
|
pipeInst->server->ClosePipeInstance(pipeInst);
|
|
}
|
|
}
|
|
|
|
void IPCServer::CompletedWriteCallback(DWORD err, DWORD bytesWritten, LPOVERLAPPED overlap)
|
|
{
|
|
PipeInstance *pipeInst = (PipeInstance *) overlap;
|
|
BOOL success = FALSE;
|
|
|
|
if (err == 0 && bytesWritten == sizeof protocol::Response)
|
|
{
|
|
success = ReadFileEx(
|
|
pipeInst->pipe,
|
|
&pipeInst->request,
|
|
sizeof protocol::Request,
|
|
overlap,
|
|
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadCallback
|
|
);
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
LOG("IPC client disconnecting due to error (via CompletedWriteCallback), error: %d, bytesWritten: %d", err, bytesWritten);
|
|
pipeInst->server->ClosePipeInstance(pipeInst);
|
|
}
|
|
}
|