mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] project: organize source layout
Move cadence and pipeline declarations under capture, and move local named-pipe handling under ipc. Flatten display contexts and rename files after their contained classes. Keep transport headers limited to transport contracts and capabilities. Consolidate CSwapChainProcessor definitions so each source file matches the class it implements.
This commit is contained in:
321
idd/LGIdd/ipc/CPipeServer.cpp
Normal file
321
idd/LGIdd/ipc/CPipeServer.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "ipc/CPipeServer.h"
|
||||
#include "CDebug.h"
|
||||
#include "display/CDeviceContext.h"
|
||||
|
||||
CPipeServer g_pipe;
|
||||
|
||||
bool CPipeServer::Init()
|
||||
{
|
||||
_DeInit();
|
||||
|
||||
m_pipe.Attach(CreateNamedPipeA(
|
||||
LG_PIPE_NAME,
|
||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1,
|
||||
1024,
|
||||
1024,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
if (!m_pipe.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create the named pipe");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_signal.Attach(CreateEvent(NULL, TRUE, FALSE, NULL));
|
||||
if (!m_signal.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create pipe signal event");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
m_thread.Attach(CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
_pipeThread,
|
||||
(LPVOID)this,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
if (!m_thread.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create the pipe thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Pipe Initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPipeServer::_DeInit()
|
||||
{
|
||||
m_running = false;
|
||||
m_connected = false;
|
||||
if (m_signal.IsValid())
|
||||
SetEvent(m_signal.Get());
|
||||
|
||||
if (m_thread.IsValid())
|
||||
{
|
||||
WaitForSingleObject(m_thread.Get(), INFINITE);
|
||||
m_thread.Close();
|
||||
}
|
||||
|
||||
if (m_pipe.IsValid())
|
||||
{
|
||||
FlushFileBuffers(m_pipe.Get());
|
||||
m_pipe.Close();
|
||||
}
|
||||
|
||||
m_signal.Close();
|
||||
}
|
||||
|
||||
void CPipeServer::DeInit()
|
||||
{
|
||||
DEBUG_TRACE("Pipe Stopping");
|
||||
_DeInit();
|
||||
DEBUG_TRACE("Pipe Stopped");
|
||||
}
|
||||
|
||||
void CPipeServer::Thread()
|
||||
{
|
||||
DEBUG_TRACE("Pipe thread started");
|
||||
|
||||
HandleT<EventTraits> ioEvent(CreateEvent(NULL, TRUE, FALSE, NULL));
|
||||
if (!ioEvent.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Can't create event for overlapped I/O!");
|
||||
WaitForSingleObject(m_signal.Get(), 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
while(m_running)
|
||||
{
|
||||
m_connected = false;
|
||||
|
||||
OVERLAPPED overlapped = { 0 };
|
||||
overlapped.hEvent = ioEvent.Get();
|
||||
|
||||
if (!ConnectNamedPipe(m_pipe.Get(), &overlapped))
|
||||
{
|
||||
DWORD dwError = GetLastError();
|
||||
switch (dwError) {
|
||||
case ERROR_PIPE_CONNECTED:
|
||||
break;
|
||||
case ERROR_IO_PENDING:
|
||||
{
|
||||
HANDLE hWait[] = { ioEvent.Get(), m_signal.Get() };
|
||||
switch (WaitForMultipleObjects(2, hWait, FALSE, INFINITE))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
DEBUG_INFO("Connect interrupted by signal");
|
||||
CancelIo(m_pipe.Get());
|
||||
WaitForSingleObject(ioEvent.Get(), INFINITE);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DEBUG_ERROR_HR(dwError, "Error connecting to the named pipe");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Client connected");
|
||||
|
||||
m_connected = true;
|
||||
|
||||
for (const auto& msg : m_queue)
|
||||
WriteMsg(msg);
|
||||
m_queue.clear();
|
||||
|
||||
while (m_running && m_connected)
|
||||
{
|
||||
LGPipeMsg msg;
|
||||
|
||||
if (!ReadFile(m_pipe.Get(), &msg, sizeof(msg), NULL, &overlapped))
|
||||
{
|
||||
DWORD dwError = GetLastError();
|
||||
if (dwError != ERROR_IO_PENDING)
|
||||
{
|
||||
DEBUG_ERROR_HR(dwError, "ReadFile Failed");
|
||||
break;
|
||||
}
|
||||
|
||||
HANDLE hWait[] = { ioEvent.Get(), m_signal.Get() };
|
||||
switch (WaitForMultipleObjects(2, hWait, FALSE, INFINITE))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
DEBUG_INFO("I/O interrupted by signal");
|
||||
CancelIo(m_pipe.Get());
|
||||
WaitForSingleObject(ioEvent.Get(), INFINITE);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD bytesRead;
|
||||
GetOverlappedResult(m_pipe.Get(), &overlapped, &bytesRead, TRUE);
|
||||
|
||||
if (bytesRead != sizeof(msg))
|
||||
{
|
||||
DEBUG_ERROR("Corrupted data, expected %lld bytes, read %lld bytes", sizeof msg, bytesRead);
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg.size != sizeof(msg))
|
||||
{
|
||||
DEBUG_ERROR("Corrupted data, expected %lld bytes, actual message size: %lld bytes", sizeof msg, msg.size);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (msg.type)
|
||||
{
|
||||
case LGPipeMsg::RELOADSETTINGS:
|
||||
HandleReloadSettings();
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unknown message type %d", msg.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Client disconnected");
|
||||
DisconnectNamedPipe(m_pipe.Get());
|
||||
|
||||
if (m_running)
|
||||
ResetEvent(m_signal.Get());
|
||||
}
|
||||
|
||||
end:
|
||||
m_running = false;
|
||||
m_connected = false;
|
||||
DEBUG_TRACE("Pipe thread shutdown");
|
||||
}
|
||||
|
||||
void CPipeServer::WriteMsg(const LGPipeMsg & msg)
|
||||
{
|
||||
if (!m_connected)
|
||||
{
|
||||
// Not connected yet: keep only the latest message of each type. These are
|
||||
// all latest-state-wins messages, so a burst (e.g. display mode changes
|
||||
// while resizing) must collapse to the final state rather than replay every
|
||||
// intermediate value when the helper reconnects.
|
||||
for (auto & queued : m_queue)
|
||||
if (queued.type == msg.type)
|
||||
{
|
||||
queued = msg;
|
||||
return;
|
||||
}
|
||||
m_queue.push_back(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD written;
|
||||
if (!WriteFile(m_pipe.Get(), &msg, sizeof(msg), &written, NULL))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
|
||||
{
|
||||
DEBUG_WARN_HR(err, "Client disconnected, failed to write");
|
||||
m_connected = false;
|
||||
SetEvent(m_signal.Get());
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_WARN_HR(err, "WriteFile failed on the pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
FlushFileBuffers(m_pipe.Get());
|
||||
}
|
||||
|
||||
void CPipeServer::HandleReloadSettings()
|
||||
{
|
||||
DEBUG_INFO("Reloading settings");
|
||||
|
||||
AcquireSRWLockShared(&m_deviceContextLock);
|
||||
if (m_deviceContext)
|
||||
m_deviceContext->ReloadSettings();
|
||||
ReleaseSRWLockShared(&m_deviceContextLock);
|
||||
}
|
||||
|
||||
void CPipeServer::SetDeviceContext(CDeviceContext * context)
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_deviceContextLock);
|
||||
m_deviceContext = context;
|
||||
ReleaseSRWLockExclusive(&m_deviceContextLock);
|
||||
}
|
||||
|
||||
void CPipeServer::SetCursorPos(uint32_t x, uint32_t y)
|
||||
{
|
||||
// do not send cursor messages if we are not connected or they will end up queued
|
||||
if (!m_connected)
|
||||
return;
|
||||
|
||||
LGPipeMsg msg = {};
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::SETCURSORPOS;
|
||||
msg.curorPos.x = x;
|
||||
msg.curorPos.y = y;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
|
||||
void CPipeServer::SetDisplayMode(
|
||||
uint32_t width, uint32_t height, uint32_t refreshMilliHz)
|
||||
{
|
||||
LGPipeMsg msg = {};
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::SETDISPLAYMODE;
|
||||
msg.displayMode.width = width;
|
||||
msg.displayMode.height = height;
|
||||
msg.displayMode.refreshMilliHz = refreshMilliHz;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
|
||||
void CPipeServer::SetGPUStatus(bool software)
|
||||
{
|
||||
LGPipeMsg msg = {};
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::GPUSTATUS;
|
||||
msg.gpuStatus.software = software;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
|
||||
void CPipeServer::ResolutionRejected(uint32_t width, uint32_t height,
|
||||
uint32_t requiredSizeMiB)
|
||||
{
|
||||
LGPipeMsg msg = {};
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::RESOLUTIONREJECTED;
|
||||
msg.resolutionRejected.width = width;
|
||||
msg.resolutionRejected.height = height;
|
||||
msg.resolutionRejected.requiredSizeMiB = requiredSizeMiB;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
Reference in New Issue
Block a user