[idd] project: organize driver sources by responsibility

Group the IDD sources and Visual Studio filters by subsystem.

Split the device and swap-chain implementations into focused units,
rename the context classes, and reduce header coupling.
This commit is contained in:
Geoffrey McRae
2026-08-07 14:38:36 +10:00
parent 3ddc199bec
commit 30a1383d5e
76 changed files with 5067 additions and 3923 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
/**
* 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
*/
#pragma once
#include <Windows.h>
#include <atomic>
#include <stdint.h>
extern "C" {
#include "lgmp/host.h"
}
#include "capture/CFrameScheduler.h"
#include "capture/FrameBufferTypes.h"
#include "common/KVMFR.h"
#include "postprocess/D12FrameFormat.h"
#include "transport/FrameMemoryLimits.h"
class CIVSHMEM;
class CLGMPHost;
class CFrameTransport
{
public:
struct SubscriberSnapshot
{
uint32_t clientIDs [LGMP_MAX_CLIENTS] = {};
uint32_t ownerClientIDs[LGMP_MAX_CLIENTS] = {};
unsigned clientCount = 0;
unsigned ownerClientCount = 0;
LGMP_STATUS status = LGMP_OK;
};
private:
enum SharedFramePostResult
{
SHARED_FRAME_FAILED,
SHARED_FRAME_IDLE,
SHARED_FRAME_POSTED,
};
struct FrameDelivery
{
uint64_t sharedOwnerToken = 0;
unsigned ownerQueueMask = 0;
uint32_t sharedOwnerClientID = 0;
bool sharedOwnerPending = false;
bool sharedPending = false;
};
struct OwnerDelivery
{
uint64_t token = 0;
uint32_t clientID = 0;
unsigned frameIndex = 0;
bool active = false;
};
CLGMPHost& m_host;
CIVSHMEM& m_ivshmem;
PLGMPHostQueue m_frameQueue = nullptr;
PLGMPHostQueue m_frameOwnerQueue[LGMP_Q_FRAME_LEN] = {};
CFrameScheduler m_frameScheduler;
size_t m_alignSize = 0;
size_t m_frameMemoryOffset = 0;
size_t m_maxFrameSize = 0;
// LGMP publication precedes copy completion. Replay only completed frames;
// the deferred index tracks the newest frame still owed to the owner.
std::atomic<LONG> m_submittedFrameIndex = -1;
std::atomic<LONG> m_readyFrameIndex = -1;
LONG m_deferredOwnerFrameIndex = -1;
std::atomic<bool> m_frameInFlight[LGMP_Q_FRAME_BUFFER_LEN] = {};
bool m_frameCompleted[LGMP_Q_FRAME_BUFFER_LEN] = {};
SRWLOCK m_framePublishLock = SRWLOCK_INIT;
uint64_t m_framePublishSequence = 0;
uint64_t m_frameLastPublishSequence[LGMP_Q_FRAME_BUFFER_LEN] = {};
FrameDelivery m_frameDelivery[LGMP_Q_FRAME_BUFFER_LEN] = {};
OwnerDelivery m_ownerDelivery[LGMP_Q_FRAME_LEN] = {};
uint32_t m_formatVer = 0;
uint32_t m_frameSerial = 0;
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_BUFFER_LEN] = {};
KVMFRFrame * m_frame [LGMP_Q_FRAME_BUFFER_LEN] = {};
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_BUFFER_LEN] = {};
unsigned m_width = 0;
unsigned m_height = 0;
unsigned m_frameWidth = 0;
unsigned m_frameHeight = 0;
unsigned m_pitch = 0;
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
FrameType m_frameType = FRAME_TYPE_INVALID;
// Previous HDR metadata used to detect changes for formatVer bumps.
uint16_t m_lastHDRDisplayPrimary[3][2] = {};
uint16_t m_lastHDRWhitePoint[2] = {};
uint32_t m_lastHDRMaxDisplayLuminance = 0;
uint32_t m_lastHDRMinDisplayLuminance = 0;
uint32_t m_lastHDRMaxContentLightLevel = 0;
uint32_t m_lastHDRMaxFrameAverageLightLevel = 0;
uint32_t m_lastSDRWhiteLevel = 0;
bool m_lastHDRActive = false;
bool m_lastHDRMetadata = false;
void ProcessFrameDeliveries();
bool FrameBufferReferenced(unsigned frameIndex) const;
int FindAvailableFrameBuffer(bool allowReady) const;
int FindNewestCompletedFrame(unsigned excludeFrameIndex) const;
int FindAvailableOwnerQueue(unsigned preferredIndex) const;
unsigned CountOwnerDeliveries(uint32_t clientID) const;
bool HasMatchingOwnerDelivery(uint32_t clientID, unsigned frameIndex,
uint64_t token) const;
SharedFramePostResult PostSharedFrame(unsigned frameIndex,
uint32_t excludeClientID, uint64_t now);
bool PostSharedOwnerFrame(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule);
public:
CFrameTransport(CLGMPHost& host, CIVSHMEM& ivshmem);
~CFrameTransport();
CFrameTransport(const CFrameTransport&) = delete;
CFrameTransport& operator=(const CFrameTransport&) = delete;
bool Initialize();
void SealMemoryLayout();
bool Setup(size_t alignSize);
void DeInit();
FrameMemoryLimits GetMemoryLimits() const;
size_t GetMaxFrameSize() const { return m_maxFrameSize; }
CIVSHMEM& GetIVSHMEM() { return m_ivshmem; }
SubscriberSnapshot SnapshotSubscribers() const;
void FinalizeSubscribers(
const SubscriberSnapshot& snapshot, uint64_t now);
bool UpdateSchedule(uint32_t sourceClientID,
const KVMFRFrameSchedule& schedule, uint64_t now);
bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule,
bool allowReadyReplacement = true);
bool HasPublishedFrame() const
{
return m_readyFrameIndex.load(std::memory_order_acquire) >= 0;
}
void ProcessFrameQueue();
bool GetSharedFrameTarget(uint64_t now, uint64_t& target);
bool ReplaySharedFrame(uint64_t now, bool& retry);
PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch,
const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat,
const RECT * dirtyRects, unsigned nbDirtyRects,
const CFrameScheduler::Schedule& schedule,
bool allowReadyReplacement = true);
bool PublishFrameBuffer(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner);
bool RepublishFrameBuffer(const CFrameScheduler::Schedule& schedule);
bool TryFrameSubmitted(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule);
void CommitFrameBuffer(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule, bool periodic,
bool deliveredToOwner);
void AbortFrameBuffer(unsigned frameIndex);
void FailFrameBuffer(unsigned frameIndex);
void CompleteFrameBuffer(unsigned frameIndex, bool succeeded);
void SetFrameTiming(unsigned frameIndex, uint64_t captureTime,
uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime,
uint64_t holdTime, const CFrameScheduler::Schedule& schedule,
uint64_t completedAt);
void WriteFrameBuffer(unsigned frameIndex, void * src, size_t offset,
size_t len, bool setWritePos) const;
void WriteFrameBufferRows(unsigned frameIndex, void * src,
size_t offset, size_t rowBytes, size_t pitch, unsigned rows) const;
void FinalizeFrameBuffer(unsigned frameIndex) const;
void ObserveFrame(uint64_t now);
void ForceFrame();
bool GetPublishTarget(uint64_t now, uint64_t& target,
CFrameScheduler::Schedule& schedule, bool& periodic, bool& republish);
void FrameMissed(const CFrameScheduler::Schedule& schedule,
uint64_t now, bool periodic);
void FrameSuperseded();
HANDLE GetFrameScheduleEvent() const
{
return m_frameScheduler.GetWakeEvent();
}
void TryRecordFrameTiming(uint64_t duration);
};

View File

@@ -0,0 +1,208 @@
/**
* 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 "transport/CIVSHMEM.h"
#include <Windows.h>
#include <SetupAPI.h>
#include <algorithm>
#include <winioctl.h>
#include "CDebug.h"
#include "ivshmem/ivshmem.h"
CIVSHMEM::CIVSHMEM()
{
}
CIVSHMEM::~CIVSHMEM()
{
if (m_handle == INVALID_HANDLE_VALUE)
return;
Close();
CloseHandle(m_handle);
}
bool CIVSHMEM::Init()
{
// Init may be called more than once (the adapter init is retried at boot
// until IVSHMEM enumerates). Release any handle from a prior attempt so we
// do not leak it when re-enumerating.
if (m_handle != INVALID_HANDLE_VALUE)
{
Close();
CloseHandle(m_handle);
m_handle = INVALID_HANDLE_VALUE;
}
HDEVINFO devInfoSet;
SP_DEVINFO_DATA devInfoData;
SP_DEVICE_INTERFACE_DATA devInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA infData = nullptr;
devInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IVSHMEM, nullptr, nullptr,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
devInfoData.cbSize = sizeof(devInfoData);
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
m_devices.clear();
for (int i = 0; SetupDiEnumDeviceInfo(devInfoSet, i, &devInfoData); ++i)
{
DWORD bus, addr;
if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_BUSNUMBER,
nullptr, (BYTE*)&bus, sizeof(bus), nullptr))
bus = 0xffff;
if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_ADDRESS,
nullptr, (BYTE*)&addr, sizeof(addr), nullptr))
addr = 0xffff;
IVSHMEMData data;
data.busAddr = ((DWORD64)bus) << 32 | addr;
memcpy(&data.devInfoData, &devInfoData, sizeof(devInfoData));
m_devices.push_back(data);
}
HRESULT hr = GetLastError();
if (hr != ERROR_NO_MORE_ITEMS)
{
m_devices.clear();
SetupDiDestroyDeviceInfoList(devInfoSet);
DEBUG_ERROR_HR(hr, "Enumeration Failed");
return false;
}
std::sort(m_devices.begin(), m_devices.end(),
[](const IVSHMEMData & a, const IVSHMEMData & b) -> bool
{ return a.busAddr < b.busAddr; });
HKEY hkeyLG;
IVSHMEMData * device = nullptr;
DWORD shmDevice = 0;
if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Looking Glass", &hkeyLG) == ERROR_SUCCESS)
{
DWORD dataType;
DWORD dataSize = sizeof(shmDevice);
if (RegQueryValueExA(hkeyLG, "shmDevice", nullptr, &dataType, (BYTE*)&shmDevice, &dataSize) != ERROR_SUCCESS ||
dataType != REG_DWORD)
shmDevice = 0;
}
DWORD i = 0;
for (auto it = m_devices.begin(); it != m_devices.end(); ++it, ++i)
{
DWORD bus = it->busAddr >> 32;
DWORD addr = it->busAddr & 0xFFFFFFFF;
DEBUG_INFO("IVSHMEM %u%c on bus 0x%lx, device 0x%lx, function 0x%lx",
i, i == shmDevice ? '*' : ' ', bus, addr >> 16, addr & 0xFFFF);
if (i == shmDevice)
device = &(*it);
}
if (!device)
{
DEBUG_ERROR("Failed to match a shmDevice");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
if (SetupDiEnumDeviceInterfaces(devInfoSet, &devInfoData, &GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE)
{
DEBUG_ERROR_HR(GetLastError(), "SetupDiEnumDeviceInterfaces");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
DWORD reqSize = 0;
SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, nullptr, 0, &reqSize, nullptr);
if (!reqSize)
{
DEBUG_ERROR_HR(GetLastError(), "SetupDiGetDeviceInterfaceDetail");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(1, reqSize);
infData->cbSize = sizeof(PSP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, infData, reqSize, nullptr, nullptr))
{
DEBUG_ERROR_HR(GetLastError(), "SetupDiGetDeviceInterfaceDetail");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
m_handle = CreateFile(infData->DevicePath, 0, 0, nullptr, OPEN_EXISTING, 0, 0);
if (m_handle == INVALID_HANDLE_VALUE)
{
DEBUG_ERROR_HR(GetLastError(), "CreateFile");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
SetupDiDestroyDeviceInfoList(devInfoSet);
DEBUG_TRACE("IVSHMEM Initialized");
return true;
}
bool CIVSHMEM::Open()
{
IVSHMEM_SIZE size;
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_SIZE, nullptr, 0, &size, sizeof(size), nullptr, nullptr))
{
DEBUG_ERROR_HR(GetLastError(), "Failed to request ivshmem size");
return false;
}
IVSHMEM_MMAP_CONFIG config = {};
IVSHMEM_MMAP map = {};
config.cacheMode = IVSHMEM_CACHE_WRITECOMBINED;
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_MMAP, &config, sizeof(config), &map, sizeof(map), nullptr, nullptr))
{
DEBUG_ERROR_HR(GetLastError(), "Failed to request ivshmem mmap");
return false;
}
m_size = (size_t)size;
m_mem = map.ptr;
DEBUG_INFO("IVSHMEM opened, size: %zu MiB", m_size / 1048576);
return true;
}
void CIVSHMEM::Close()
{
if (m_mem == nullptr)
return;
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_RELEASE_MMAP, nullptr, 0, nullptr, 0, nullptr, nullptr))
{
DEBUG_ERROR("Failed to release ivshmem mmap");
return;
}
m_size = 0;
m_mem = nullptr;
}

View File

@@ -0,0 +1,51 @@
/**
* 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
*/
#pragma once
#include <Windows.h>
#include <SetupAPI.h>
#include <vector>
class CIVSHMEM
{
private:
struct IVSHMEMData
{
SP_DEVINFO_DATA devInfoData;
DWORD64 busAddr;
};
std::vector<struct IVSHMEMData> m_devices;
HANDLE m_handle = INVALID_HANDLE_VALUE;
size_t m_size = 0;
void * m_mem = nullptr;
public:
CIVSHMEM();
~CIVSHMEM();
bool Init();
bool Open();
void Close();
size_t GetSize() const { return m_size; }
void * GetMem () { return m_mem; }
};

View File

@@ -0,0 +1,301 @@
/**
* 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 "transport/CLGMPControl.h"
#include "CDebug.h"
#include <string.h>
#include <utility>
static const uint32_t MAX_POINTER_SIZE =
(uint32_t)(sizeof(KVMFRCursor) + (512 * 512 * 4));
static const struct LGMPQueueConfig POINTER_QUEUE_CONFIG =
{
LGMP_Q_POINTER, //queueID
LGMP_Q_POINTER_LEN, //numMesages
1000 //subTimeout
};
CLGMPControl::~CLGMPControl()
{
DeInit();
}
bool CLGMPControl::Initialize()
{
if (m_pointerQueue)
return true;
LGMP_STATUS status;
if ((status = m_host.CreateQueue(
POINTER_QUEUE_CONFIG, &m_pointerQueue)) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostQueueCreate Failed (Pointer): %s",
lgmpStatusString(status));
return false;
}
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
{
if ((status = m_host.Allocate(
MAX_POINTER_SIZE, &m_pointerMemory[i])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s",
lgmpStatusString(status));
return false;
}
memset(lgmpHostMemPtr(m_pointerMemory[i]), 0, MAX_POINTER_SIZE);
}
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
{
if ((status = m_host.Allocate(
MAX_POINTER_SIZE, &m_pointerShapeMemory[i])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shapes): %s",
lgmpStatusString(status));
return false;
}
memset(lgmpHostMemPtr(m_pointerShapeMemory[i]), 0, MAX_POINTER_SIZE);
}
for (int i = 0; i < COLOR_TRANSFORM_BUFFERS; ++i)
{
if ((status = m_host.Allocate(
sizeof(KVMFRCursor) + sizeof(KVMFRColorTransform),
&m_pointerTransformMemory[i])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Transform): %s",
lgmpStatusString(status));
return false;
}
memset(lgmpHostMemPtr(m_pointerTransformMemory[i]), 0,
sizeof(KVMFRCursor) + sizeof(KVMFRColorTransform));
}
return true;
}
void CLGMPControl::DeInit()
{
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
lgmpHostMemFree(&m_pointerMemory[i]);
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
lgmpHostMemFree(&m_pointerShapeMemory[i]);
for (int i = 0; i < COLOR_TRANSFORM_BUFFERS; ++i)
lgmpHostMemFree(&m_pointerTransformMemory[i]);
m_pointerQueue = nullptr;
m_pointerShape = nullptr;
m_pointerMemoryIndex = 0;
m_pointerShapeIndex = 0;
m_pointerTransformIndex = 0;
}
LGMP_STATUS CLGMPControl::ReadDataWithSource(void * data, size_t * size,
uint32_t * sourceClientID)
{
return lgmpHostReadDataWithSource(
m_pointerQueue, data, size, sourceClientID);
}
LGMP_STATUS CLGMPControl::AckData()
{
return lgmpHostAckData(m_pointerQueue);
}
bool CLGMPControl::HasNewSubscribers()
{
return lgmpHostQueueNewSubs(m_pointerQueue) != 0;
}
void CLGMPControl::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, UINT sdrWhiteLevel)
{
PLGMPMemory mem;
if (info.CursorShapeInfo.CursorType == IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
{
mem = m_pointerMemory[m_pointerMemoryIndex];
if (++m_pointerMemoryIndex == LGMP_Q_POINTER_LEN)
m_pointerMemoryIndex = 0;
}
else
{
mem = m_pointerShapeMemory[m_pointerShapeIndex];
if (++m_pointerShapeIndex == POINTER_SHAPE_BUFFERS)
m_pointerShapeIndex = 0;
}
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
cursor->sdrWhiteLevel = sdrWhiteLevel ?
sdrWhiteLevel : KVMFR_SDR_WHITE_LEVEL_DEFAULT;
m_cursorVisible = info.IsCursorVisible;
uint32_t flags = CURSOR_FLAG_VISIBLE_VALID;
if (info.IsCursorVisible)
{
m_cursorX = info.X;
m_cursorY = info.Y;
cursor->x = (int16_t)info.X;
cursor->y = (int16_t)info.Y;
flags |= CURSOR_FLAG_POSITION | CURSOR_FLAG_VISIBLE;
}
if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
{
memcpy(cursor + 1, data,
(size_t)info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch);
cursor->hx = (int8_t )info.CursorShapeInfo.XHot;
cursor->hy = (int8_t )info.CursorShapeInfo.YHot;
cursor->width = (uint32_t)info.CursorShapeInfo.Width;
cursor->height = (uint32_t)info.CursorShapeInfo.Height;
cursor->pitch = (uint32_t)info.CursorShapeInfo.Pitch;
switch (info.CursorShapeInfo.CursorType)
{
case IDDCX_CURSOR_SHAPE_TYPE_ALPHA:
cursor->type = CURSOR_TYPE_COLOR;
break;
case IDDCX_CURSOR_SHAPE_TYPE_MASKED_COLOR:
cursor->type = CURSOR_TYPE_MASKED_COLOR;
break;
}
flags |= CURSOR_FLAG_SHAPE;
m_pointerShape = mem;
}
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(
m_pointerQueue, flags, mem)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
lgmpStatusString(status));
break;
}
}
void CLGMPControl::SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform)
{
AcquireSRWLockExclusive(&m_colorTransformLock);
m_colorTransform = std::move(transform);
ReleaseSRWLockExclusive(&m_colorTransformLock);
SendColorTransform();
}
std::shared_ptr<const D12ColorTransform>
CLGMPControl::GetColorTransform() const
{
AcquireSRWLockShared(&m_colorTransformLock);
std::shared_ptr<const D12ColorTransform> transform = m_colorTransform;
ReleaseSRWLockShared(&m_colorTransformLock);
return transform;
}
void CLGMPControl::SendColorTransform()
{
if (!m_pointerQueue || !m_pointerTransformMemory[0])
return;
PLGMPMemory mem = m_pointerTransformMemory[m_pointerTransformIndex];
if (++m_pointerTransformIndex == COLOR_TRANSFORM_BUFFERS)
m_pointerTransformIndex = 0;
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
KVMFRColorTransform * output =
(KVMFRColorTransform *)(cursor + 1);
const auto transform = GetColorTransform();
output->flags = 0;
if (transform)
{
if (transform->matrixEnabled)
output->flags |= KVMFR_COLOR_TRANSFORM_MATRIX;
if (transform->lutEnabled)
output->flags |= KVMFR_COLOR_TRANSFORM_LUT;
memcpy(output->matrix, transform->matrix, sizeof(output->matrix));
output->scalar = transform->scalar;
memcpy(output->lut, transform->lut, sizeof(output->lut));
}
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(m_pointerQueue,
CURSOR_FLAG_COLOR_TRANSFORM, mem)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer Transform): %s",
lgmpStatusString(status));
break;
}
}
void CLGMPControl::ResendCursor()
{
PLGMPMemory mem = m_pointerShape;
if (!mem)
return;
KVMFRCursor* cursor = (KVMFRCursor*)lgmpHostMemPtr(mem);
cursor->x = (int16_t)m_cursorX;
cursor->y = (int16_t)m_cursorY;
const uint32_t flags =
CURSOR_FLAG_POSITION | CURSOR_FLAG_SHAPE | CURSOR_FLAG_VISIBLE_VALID |
(m_cursorVisible ? CURSOR_FLAG_VISIBLE : 0);
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(
m_pointerQueue, flags, mem)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
lgmpStatusString(status));
break;
}
}
void CLGMPControl::ResendState()
{
ResendCursor();
SendColorTransform();
}

View File

@@ -0,0 +1,82 @@
/**
* 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
*/
#pragma once
#include "transport/CLGMPHost.h"
#include "postprocess/D12FrameFormat.h"
#include "common/KVMFR.h"
#include <Windows.h>
#include <wdf.h>
#include <IddCx.h>
#include <memory>
class CLGMPControl
{
private:
static constexpr int POINTER_SHAPE_BUFFERS = 3;
static constexpr int COLOR_TRANSFORM_BUFFERS = 3;
CLGMPHost& m_host;
PLGMPHostQueue m_pointerQueue = nullptr;
PLGMPMemory m_pointerMemory[LGMP_Q_POINTER_LEN] = {};
PLGMPMemory m_pointerShapeMemory[POINTER_SHAPE_BUFFERS] = {};
PLGMPMemory m_pointerTransformMemory[COLOR_TRANSFORM_BUFFERS] = {};
PLGMPMemory m_pointerShape = nullptr;
int m_pointerMemoryIndex = 0;
int m_pointerShapeIndex = 0;
int m_pointerTransformIndex = 0;
bool m_cursorVisible = false;
int m_cursorX = 0;
int m_cursorY = 0;
mutable SRWLOCK m_colorTransformLock = SRWLOCK_INIT;
std::shared_ptr<const D12ColorTransform> m_colorTransform;
void SendColorTransform();
void ResendCursor();
public:
explicit CLGMPControl(CLGMPHost& host) :
m_host(host) {}
~CLGMPControl();
CLGMPControl(const CLGMPControl&) = delete;
CLGMPControl& operator=(const CLGMPControl&) = delete;
bool Initialize();
void DeInit();
LGMP_STATUS ReadDataWithSource(void * data, size_t * size,
uint32_t * sourceClientID);
LGMP_STATUS AckData();
bool HasNewSubscribers();
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data,
UINT sdrWhiteLevel);
void SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform);
std::shared_ptr<const D12ColorTransform> GetColorTransform() const;
void ResendState();
};

View File

@@ -0,0 +1,165 @@
/**
* 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 "transport/CLGMPHost.h"
#include "transport/CIVSHMEM.h"
#include "platform/CPlatformInfo.h"
#include "CDebug.h"
#include "VersionInfo.h"
#include "common/KVMFR.h"
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <string>
CLGMPHost::~CLGMPHost()
{
DeInit();
}
bool CLGMPHost::Initialize(CIVSHMEM& ivshmem)
{
if (m_host)
return true;
std::stringstream ss;
{
KVMFR kvmfr = {};
memcpy_s(kvmfr.magic, sizeof(kvmfr.magic), KVMFR_MAGIC, sizeof(KVMFR_MAGIC) - 1);
kvmfr.version = KVMFR_VERSION;
kvmfr.features =
KVMFR_FEATURE_SETCURSORPOS |
KVMFR_FEATURE_WINDOWSIZE |
KVMFR_FEATURE_FRAME_SCHEDULE;
strncpy_s(kvmfr.hostver, LG_VERSION_STR, sizeof(kvmfr.hostver) - 1);
ss.write(reinterpret_cast<const char *>(&kvmfr), sizeof(kvmfr));
}
{
const std::string & model = CPlatformInfo::GetCPUModel();
KVMFRRecord_VMInfo * vmInfo = static_cast<KVMFRRecord_VMInfo *>(calloc(1, sizeof(*vmInfo)));
if (!vmInfo)
{
DEBUG_ERROR("Failed to allocate KVMFRRecord_VMInfo");
return false;
}
vmInfo->cpus = static_cast<uint8_t>(CPlatformInfo::GetProcCount ());
vmInfo->cores = static_cast<uint8_t>(CPlatformInfo::GetCoreCount ());
vmInfo->sockets = static_cast<uint8_t>(CPlatformInfo::GetSocketCount());
const uint8_t * uuid = CPlatformInfo::GetUUID();
memcpy_s (vmInfo->uuid, sizeof(vmInfo->uuid), uuid, 16);
strncpy_s(vmInfo->capture, "Looking Glass IDD Driver", sizeof(vmInfo->capture));
KVMFRRecord * record = static_cast<KVMFRRecord *>(calloc(1, sizeof(*record)));
if (!record)
{
DEBUG_ERROR("Failed to allocate KVMFRRecord");
return false;
}
record->type = KVMFR_RECORD_VMINFO;
record->size = sizeof(*vmInfo) + (uint32_t)model.length() + 1;
ss.write(reinterpret_cast<const char*>(record ), sizeof(*record));
ss.write(reinterpret_cast<const char*>(vmInfo ), sizeof(*vmInfo));
ss.write(reinterpret_cast<const char*>(model.c_str()), model.length() + 1);
}
{
KVMFRRecord_OSInfo * osInfo = static_cast<KVMFRRecord_OSInfo *>(calloc(1, sizeof(*osInfo)));
if (!osInfo)
{
DEBUG_ERROR("Failed to allocate KVMFRRecord_OSInfo");
return false;
}
osInfo->os = KVMFR_OS_WINDOWS;
const std::string & osName = CPlatformInfo::GetProductName();
KVMFRRecord* record = static_cast<KVMFRRecord*>(calloc(1, sizeof(*record)));
if (!record)
{
DEBUG_ERROR("Failed to allocate KVMFRRecord");
return false;
}
record->type = KVMFR_RECORD_OSINFO;
record->size = sizeof(*osInfo) + (uint32_t)osName.length() + 1;
ss.write(reinterpret_cast<const char*>(record), sizeof(*record));
ss.write(reinterpret_cast<const char*>(osInfo), sizeof(*osInfo));
ss.write(reinterpret_cast<const char*>(osName.c_str()), osName.length() + 1);
}
LGMP_STATUS status;
std::string udata = ss.str();
if ((status = lgmpHostInit(ivshmem.GetMem(), (uint32_t)ivshmem.GetSize(),
&m_host, (uint32_t)udata.size(), (uint8_t*)&udata[0])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostInit Failed: %s", lgmpStatusString(status));
return false;
}
return true;
}
void CLGMPHost::DeInit()
{
if (m_host)
lgmpHostFree(&m_host);
}
LGMP_STATUS CLGMPHost::Process()
{
AcquireSRWLockExclusive(&m_processLock);
const LGMP_STATUS status = lgmpHostProcess(m_host);
ReleaseSRWLockExclusive(&m_processLock);
return status;
}
LGMP_STATUS CLGMPHost::CreateQueue(
const struct LGMPQueueConfig& config, PLGMPHostQueue * queue)
{
return lgmpHostQueueNew(m_host, config, queue);
}
LGMP_STATUS CLGMPHost::Allocate(uint32_t size, PLGMPMemory * memory)
{
return lgmpHostMemAlloc(m_host, size, memory);
}
LGMP_STATUS CLGMPHost::AllocateAligned(uint32_t size,
uint32_t alignment, PLGMPMemory * memory)
{
return lgmpHostMemAllocAligned(m_host, size, alignment, memory);
}
size_t CLGMPHost::Available() const
{
return lgmpHostMemAvail(m_host);
}

View File

@@ -0,0 +1,59 @@
/**
* 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
*/
#pragma once
#include <Windows.h>
#include <stddef.h>
#include <stdint.h>
extern "C" {
#include "lgmp/host.h"
}
class CIVSHMEM;
class CLGMPHost
{
private:
PLGMPHost m_host = nullptr;
SRWLOCK m_processLock = SRWLOCK_INIT;
public:
CLGMPHost() = default;
~CLGMPHost();
CLGMPHost(const CLGMPHost&) = delete;
CLGMPHost& operator=(const CLGMPHost&) = delete;
bool Initialize(CIVSHMEM& ivshmem);
void DeInit();
bool IsInitialized() const { return m_host != nullptr; }
LGMP_STATUS Process();
LGMP_STATUS CreateQueue(const struct LGMPQueueConfig& config,
PLGMPHostQueue * queue);
LGMP_STATUS Allocate(uint32_t size, PLGMPMemory * memory);
LGMP_STATUS AllocateAligned(uint32_t size, uint32_t alignment,
PLGMPMemory * memory);
size_t Available() const;
};

View 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 "transport/CPipeServer.h"
#include "CDebug.h"
#include "display/device/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);
}

View File

@@ -0,0 +1,76 @@
/**
* 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
*/
#pragma once
#include <windows.h>
#include <wdf.h>
#include <stdint.h>
#include <wrl.h>
#include <vector>
#include "PipeMsg.h"
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace Microsoft::WRL::Wrappers::HandleTraits;
class CDeviceContext;
class CPipeServer
{
private:
HandleT<HANDLETraits> m_pipe;
HandleT<HANDLENullTraits> m_thread;
HandleT<EventTraits> m_signal;
std::vector<LGPipeMsg> m_queue;
bool m_running = false;
bool m_connected = false;
SRWLOCK m_deviceContextLock = SRWLOCK_INIT;
CDeviceContext * m_deviceContext = nullptr;
void _DeInit();
static DWORD WINAPI _pipeThread(LPVOID lpParam) { ((CPipeServer*)lpParam)->Thread(); return 0; }
void Thread();
void WriteMsg(const LGPipeMsg & msg);
void HandleReloadSettings();
public:
~CPipeServer() { DeInit(); }
bool Init();
void DeInit();
void SetDeviceContext(CDeviceContext * context);
void SetCursorPos(uint32_t x, uint32_t y);
void SetDisplayMode(
uint32_t width, uint32_t height, uint32_t refreshMilliHz);
void SetGPUStatus(bool software);
void ResolutionRejected(uint32_t width, uint32_t height,
uint32_t requiredSizeMiB);
};
extern CPipeServer g_pipe;

View File

@@ -0,0 +1,31 @@
/**
* 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
*/
#pragma once
#include <stdint.h>
struct FrameMemoryLimits
{
uint64_t sharedSize = 0;
uint64_t frameMemoryOffset = 0;
uint64_t alignment = 0;
uint64_t maxFrameSize = 0;
};