mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-06-04 13:54:26 +00:00
[idd] CEdid: add new helper class to build the EDID block
This commit is contained in:
committed by
Geoffrey McRae
parent
83bebf4519
commit
c2add993ac
193
idd/LGIdd/CEdid.cpp
Normal file
193
idd/LGIdd/CEdid.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "CEdid.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
|
||||
static const UINT EDID_BLOCK_SIZE = 128;
|
||||
static const UINT EDID_DTD_SIZE = 18;
|
||||
|
||||
void CEdid::SetChecksum(BYTE* block)
|
||||
{
|
||||
BYTE sum = 0;
|
||||
for (UINT i = 0; i < EDID_BLOCK_SIZE - 1; ++i)
|
||||
sum = (BYTE)(sum + block[i]);
|
||||
block[EDID_BLOCK_SIZE - 1] = (BYTE)(0 - sum);
|
||||
}
|
||||
|
||||
void CEdid::WriteMonitorName(BYTE* desc, const char* name)
|
||||
{
|
||||
memset(desc, 0, EDID_DTD_SIZE);
|
||||
desc[3] = 0xfc;
|
||||
desc[4] = 0x00;
|
||||
|
||||
UINT i = 0;
|
||||
for (; i < 13 && name[i]; ++i)
|
||||
desc[5 + i] = (BYTE)name[i];
|
||||
if (i < 13)
|
||||
desc[5 + i++] = 0x0a;
|
||||
for (; i < 13; ++i)
|
||||
desc[5 + i] = 0x20;
|
||||
}
|
||||
|
||||
bool CEdid::WriteDetailedTiming(BYTE* dtd, const CSettings::DisplayMode& mode)
|
||||
{
|
||||
memset(dtd, 0, EDID_DTD_SIZE);
|
||||
|
||||
const DWORD hActive = mode.width;
|
||||
const DWORD vActive = mode.height;
|
||||
const DWORD refresh = mode.refresh;
|
||||
|
||||
if (hActive == 0 || vActive == 0 || refresh == 0 ||
|
||||
hActive > 4095 || vActive > 4095)
|
||||
return false;
|
||||
|
||||
DWORD hBlank = std::max<DWORD>(160, ((hActive / 20) + 7) & ~7UL);
|
||||
DWORD vBlank = std::max<DWORD>(30, vActive / 20);
|
||||
if (hBlank > 4095 || vBlank > 4095)
|
||||
return false;
|
||||
|
||||
DWORD hSync = std::max<DWORD>(32, hActive / 100);
|
||||
hSync = (hSync + 7) & ~7UL;
|
||||
DWORD hFront = std::max<DWORD>(48, hBlank / 3);
|
||||
hFront = (hFront + 7) & ~7UL;
|
||||
if (hFront + hSync >= hBlank)
|
||||
{
|
||||
hFront = 48;
|
||||
hSync = 32;
|
||||
}
|
||||
|
||||
const DWORD vFront = 3;
|
||||
const DWORD vSync = 5;
|
||||
if (vFront + vSync >= vBlank)
|
||||
return false;
|
||||
|
||||
const UINT64 pixelClock = (UINT64)(hActive + hBlank) *
|
||||
(UINT64)(vActive + vBlank) * (UINT64)refresh;
|
||||
const UINT64 pixelClock10KHz = (pixelClock + 5000) / 10000;
|
||||
if (pixelClock10KHz == 0 || pixelClock10KHz > 0xffff)
|
||||
return false;
|
||||
|
||||
dtd[0] = (BYTE)(pixelClock10KHz & 0xff);
|
||||
dtd[1] = (BYTE)((pixelClock10KHz >> 8) & 0xff);
|
||||
dtd[2] = (BYTE)(hActive & 0xff);
|
||||
dtd[3] = (BYTE)(hBlank & 0xff);
|
||||
dtd[4] = (BYTE)(((hActive >> 8) & 0x0f) << 4 | ((hBlank >> 8) & 0x0f));
|
||||
dtd[5] = (BYTE)(vActive & 0xff);
|
||||
dtd[6] = (BYTE)(vBlank & 0xff);
|
||||
dtd[7] = (BYTE)(((vActive >> 8) & 0x0f) << 4 | ((vBlank >> 8) & 0x0f));
|
||||
dtd[8] = (BYTE)(hFront & 0xff);
|
||||
dtd[9] = (BYTE)(hSync & 0xff);
|
||||
dtd[10] = (BYTE)((vFront & 0x0f) << 4 | (vSync & 0x0f));
|
||||
dtd[11] = (BYTE)(((hFront >> 8) & 0x03) << 6 |
|
||||
(((hSync >> 8) & 0x03) << 4) |
|
||||
(((vFront >> 4) & 0x03) << 2) |
|
||||
((vSync >> 4) & 0x03));
|
||||
|
||||
// 52cm x 29cm is a conventional 16:9 desktop monitor size.
|
||||
const DWORD imageWidth = 520;
|
||||
const DWORD imageHeight = 290;
|
||||
dtd[12] = (BYTE)(imageWidth & 0xff);
|
||||
dtd[13] = (BYTE)(imageHeight & 0xff);
|
||||
dtd[14] = (BYTE)(((imageWidth >> 8) & 0x0f) << 4 | ((imageHeight >> 8) & 0x0f));
|
||||
dtd[17] = 0x1e; // digital separate sync, positive hsync/vsync
|
||||
return true;
|
||||
}
|
||||
|
||||
void CEdid::Build(const CSettings::DisplayModes& modes)
|
||||
{
|
||||
m_data.assign(EDID_BLOCK_SIZE * 2, 0);
|
||||
BYTE* base = m_data.data();
|
||||
BYTE* cta = base + EDID_BLOCK_SIZE;
|
||||
|
||||
static const BYTE header[8] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
|
||||
memcpy(base, header, sizeof(header));
|
||||
|
||||
// Manufacturer ID: LGD (5-bit EISA ID), product/serial values are arbitrary.
|
||||
base[8] = 0x30;
|
||||
base[9] = 0xe4;
|
||||
base[10] = 0xdd;
|
||||
base[11] = 0x1d;
|
||||
base[12] = 0x01;
|
||||
base[16] = 1;
|
||||
base[17] = 36; // 1990 + 36 = 2026
|
||||
base[18] = 1;
|
||||
base[19] = 4;
|
||||
base[20] = 0xa5; // digital input, 10-bit capable
|
||||
base[21] = 52;
|
||||
base[22] = 29;
|
||||
base[23] = 0x78; // gamma 2.2
|
||||
base[24] = 0x0a; // preferred timing, RGB color
|
||||
|
||||
for (UINT i = 38; i < 54; i += 2)
|
||||
{
|
||||
base[i] = 0x01;
|
||||
base[i + 1] = 0x01;
|
||||
}
|
||||
|
||||
CSettings::DisplayModes sorted = modes;
|
||||
std::stable_sort(sorted.begin(), sorted.end(), [](const CSettings::DisplayMode& a, const CSettings::DisplayMode& b)
|
||||
{
|
||||
if (a.preferred != b.preferred)
|
||||
return a.preferred && !b.preferred;
|
||||
if (a.width != b.width)
|
||||
return a.width > b.width;
|
||||
if (a.height != b.height)
|
||||
return a.height > b.height;
|
||||
return a.refresh > b.refresh;
|
||||
});
|
||||
|
||||
UINT modeIndex = 0;
|
||||
UINT dtdOffset = 54;
|
||||
for (; modeIndex < sorted.size() && dtdOffset < 54 + EDID_DTD_SIZE * 3; ++modeIndex)
|
||||
{
|
||||
if (WriteDetailedTiming(base + dtdOffset, sorted[modeIndex]))
|
||||
dtdOffset += EDID_DTD_SIZE;
|
||||
}
|
||||
WriteMonitorName(base + 54 + EDID_DTD_SIZE * 3, "Looking Glass");
|
||||
|
||||
base[126] = 1;
|
||||
SetChecksum(base);
|
||||
|
||||
cta[0] = 0x02; // CTA-861 extension
|
||||
cta[1] = 0x03;
|
||||
|
||||
UINT dataOffset = 4;
|
||||
// CTA extended colorimetry data block: advertise BT.2020 colorimetry.
|
||||
cta[dataOffset++] = (7 << 5) | 3;
|
||||
cta[dataOffset++] = 0x05;
|
||||
cta[dataOffset++] = 0xe0;
|
||||
cta[dataOffset++] = 0x00;
|
||||
|
||||
// CTA HDR Static Metadata data block: SDR, traditional HDR, PQ and HLG with type 1 metadata.
|
||||
cta[dataOffset++] = (7 << 5) | 4;
|
||||
cta[dataOffset++] = 0x06;
|
||||
cta[dataOffset++] = 0x0f;
|
||||
cta[dataOffset++] = 0x01;
|
||||
cta[dataOffset++] = 0x00;
|
||||
|
||||
UINT ctaDtdOffset = dataOffset;
|
||||
if (ctaDtdOffset < 4)
|
||||
ctaDtdOffset = 4;
|
||||
|
||||
UINT ctaDtdWrite = ctaDtdOffset;
|
||||
for (; modeIndex < sorted.size() && ctaDtdWrite + EDID_DTD_SIZE <= EDID_BLOCK_SIZE - 1; ++modeIndex)
|
||||
{
|
||||
if (WriteDetailedTiming(cta + ctaDtdWrite, sorted[modeIndex]))
|
||||
ctaDtdWrite += EDID_DTD_SIZE;
|
||||
}
|
||||
|
||||
cta[2] = (BYTE)ctaDtdOffset;
|
||||
cta[3] = 0x00;
|
||||
SetChecksum(cta);
|
||||
}
|
||||
34
idd/LGIdd/CEdid.h
Normal file
34
idd/LGIdd/CEdid.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "CSettings.h"
|
||||
|
||||
class CEdid
|
||||
{
|
||||
public:
|
||||
void Build(const CSettings::DisplayModes& modes);
|
||||
|
||||
const BYTE* Data() const { return m_data.empty() ? nullptr : m_data.data(); }
|
||||
UINT Size() const { return (UINT)m_data.size(); }
|
||||
|
||||
private:
|
||||
std::vector<BYTE> m_data;
|
||||
|
||||
static void SetChecksum(BYTE* block);
|
||||
static bool WriteDetailedTiming(BYTE* dtd, const CSettings::DisplayMode& mode);
|
||||
static void WriteMonitorName(BYTE* desc, const char* name);
|
||||
};
|
||||
@@ -51,6 +51,8 @@ void CIndirectDeviceContext::PopulateDefaultModes()
|
||||
m_displayModes.reserve(g_settings.GetDisplayModes().size());
|
||||
for (auto& dm : g_settings.GetDisplayModes())
|
||||
m_displayModes.push_back(dm);
|
||||
|
||||
m_edid.Build(m_displayModes);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::InitAdapter()
|
||||
@@ -143,8 +145,8 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
|
||||
|
||||
info.MonitorDescription.Size = sizeof(info.MonitorDescription);
|
||||
info.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
|
||||
info.MonitorDescription.DataSize = 0;
|
||||
info.MonitorDescription.pData = nullptr;
|
||||
info.MonitorDescription.DataSize = m_edid.Size();
|
||||
info.MonitorDescription.pData = const_cast<BYTE*>(m_edid.Data());
|
||||
|
||||
CoCreateGuid(&info.MonitorContainerId);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "CIVSHMEM.h"
|
||||
#include "CSettings.h"
|
||||
#include "CEdid.h"
|
||||
|
||||
extern "C" {
|
||||
#include "lgmp/host.h"
|
||||
@@ -89,6 +90,7 @@ private:
|
||||
void ResendCursor();
|
||||
|
||||
CSettings::DisplayModes m_displayModes;
|
||||
CEdid m_edid;
|
||||
|
||||
CSettings::DisplayMode m_setMode;
|
||||
bool m_doSetMode;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
|
||||
<ClCompile Include="CD3D12CommandQueue.cpp" />
|
||||
<ClCompile Include="CEdid.cpp" />
|
||||
<ClCompile Include="CFrameBufferPool.cpp" />
|
||||
<ClCompile Include="CFrameBufferResource.cpp" />
|
||||
<ClCompile Include="CIndirectDeviceContext.cpp" />
|
||||
@@ -45,6 +46,7 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
|
||||
<ClInclude Include="CD3D12CommandQueue.h" />
|
||||
<ClInclude Include="CEdid.h" />
|
||||
<ClInclude Include="CFrameBufferPool.h" />
|
||||
<ClInclude Include="CFrameBufferResource.h" />
|
||||
<ClInclude Include="CIndirectMonitorContext.h" />
|
||||
|
||||
Reference in New Issue
Block a user