mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] config: support fractional refresh rates
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
build / client-tests (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client-tests (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client-tests (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client-tests (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client-tests (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client-tests (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client-tests (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client-tests (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
build / client-tests (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client-tests (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client-tests (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client-tests (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client-tests (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client-tests (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client-tests (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client-tests (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
Store refresh rates in millihertz and preserve three decimal places. Advertise exact rational rates while accepting legacy integer values. Accept rates from 23.900 through 1000.000 Hz.
This commit is contained in:
@@ -49,7 +49,7 @@ struct LGPipeMsg
|
|||||||
{
|
{
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
uint32_t refresh;
|
uint32_t refreshMilliHz;
|
||||||
}
|
}
|
||||||
displayMode;
|
displayMode;
|
||||||
|
|
||||||
|
|||||||
104
idd/LGCommon/RefreshRate.cpp
Normal file
104
idd/LGCommon/RefreshRate.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* 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 "RefreshRate.h"
|
||||||
|
|
||||||
|
#include <cwchar>
|
||||||
|
#include <cwctype>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
static std::wstring Trim(const std::wstring& text)
|
||||||
|
{
|
||||||
|
size_t begin = 0;
|
||||||
|
size_t end = text.size();
|
||||||
|
while (begin < end && std::iswspace(text[begin]))
|
||||||
|
++begin;
|
||||||
|
while (end > begin && std::iswspace(text[end - 1]))
|
||||||
|
--end;
|
||||||
|
return text.substr(begin, end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ToUnsigned(const std::wstring& text, unsigned& value)
|
||||||
|
{
|
||||||
|
if (text.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wchar_t * end = nullptr;
|
||||||
|
const unsigned long long parsed = std::wcstoull(text.c_str(), &end, 10);
|
||||||
|
if (!end || *end != L'\0' ||
|
||||||
|
parsed > std::numeric_limits<unsigned>::max())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
value = (unsigned)parsed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LGParseRefreshRate(const std::wstring& text, unsigned& refreshMilliHz)
|
||||||
|
{
|
||||||
|
const std::wstring value = Trim(text);
|
||||||
|
const size_t decimal = value.find(L'.');
|
||||||
|
if (value.empty() ||
|
||||||
|
(decimal != std::wstring::npos &&
|
||||||
|
(value.find(L'.', decimal + 1) != std::wstring::npos ||
|
||||||
|
decimal == 0 || decimal + 4 < value.size())))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::wstring wholeText = value.substr(0, decimal);
|
||||||
|
const std::wstring fractionText = decimal == std::wstring::npos ?
|
||||||
|
std::wstring() : value.substr(decimal + 1);
|
||||||
|
unsigned whole;
|
||||||
|
unsigned fraction = 0;
|
||||||
|
if (!ToUnsigned(wholeText, whole) ||
|
||||||
|
(decimal != std::wstring::npos && fractionText.empty()) ||
|
||||||
|
(!fractionText.empty() && !ToUnsigned(fractionText, fraction)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (fractionText.size() == 1)
|
||||||
|
fraction *= 100;
|
||||||
|
else if (fractionText.size() == 2)
|
||||||
|
fraction *= 10;
|
||||||
|
|
||||||
|
const unsigned long long valueMilliHz =
|
||||||
|
(unsigned long long)whole * 1000 + fraction;
|
||||||
|
if (valueMilliHz < 23900 || valueMilliHz > 1000000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
refreshMilliHz = (unsigned)valueMilliHz;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring LGFormatRefreshRate(unsigned refreshMilliHz)
|
||||||
|
{
|
||||||
|
std::wstring result = std::to_wstring(refreshMilliHz / 1000);
|
||||||
|
const unsigned fraction = refreshMilliHz % 1000;
|
||||||
|
if (!fraction)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
const wchar_t text[] =
|
||||||
|
{
|
||||||
|
L'.',
|
||||||
|
(wchar_t)(L'0' + fraction / 100),
|
||||||
|
(wchar_t)(L'0' + fraction / 10 % 10),
|
||||||
|
(wchar_t)(L'0' + fraction % 10),
|
||||||
|
L'\0'
|
||||||
|
};
|
||||||
|
result.append(text);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
26
idd/LGCommon/RefreshRate.h
Normal file
26
idd/LGCommon/RefreshRate.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* 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 <string>
|
||||||
|
|
||||||
|
bool LGParseRefreshRate(const std::wstring& text, unsigned& refreshMilliHz);
|
||||||
|
std::wstring LGFormatRefreshRate(unsigned refreshMilliHz);
|
||||||
@@ -86,8 +86,8 @@ static const BYTE CTA_COLORIMETRY_BT2020_RGB = (BYTE)(1 << 7);
|
|||||||
// changing these timings at runtime can make it treat the IDD as a new monitor.
|
// changing these timings at runtime can make it treat the IDD as a new monitor.
|
||||||
static const CSettings::DisplayMode EDID_DISPLAY_MODES[] =
|
static const CSettings::DisplayMode EDID_DISPLAY_MODES[] =
|
||||||
{
|
{
|
||||||
{ 1024, 768, 60, true , false },
|
{ 1024, 768, 60000, true , false },
|
||||||
{ 800, 600, 60, false, false }
|
{ 800, 600, 60000, false, false }
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
@@ -366,7 +366,8 @@ bool CEdid::GetTiming(Timing& timing, const CSettings::DisplayMode& mode)
|
|||||||
timing.hActive = mode.width;
|
timing.hActive = mode.width;
|
||||||
timing.vActive = mode.height;
|
timing.vActive = mode.height;
|
||||||
|
|
||||||
if (timing.hActive == 0 || timing.vActive == 0 || mode.refresh == 0)
|
if (timing.hActive == 0 || timing.vActive == 0 ||
|
||||||
|
mode.refreshMilliHz == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
timing.hBlank = std::max<DWORD>(160,
|
timing.hBlank = std::max<DWORD>(160,
|
||||||
@@ -390,12 +391,11 @@ bool CEdid::GetTiming(Timing& timing, const CSettings::DisplayMode& mode)
|
|||||||
if (timing.vFront + timing.vSync >= timing.vBlank)
|
if (timing.vFront + timing.vSync >= timing.vBlank)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const UINT64 pixelClock =
|
const UINT64 pixelClockMilliHz =
|
||||||
(UINT64)(timing.hActive + timing.hBlank) *
|
(UINT64)(timing.hActive + timing.hBlank) *
|
||||||
(UINT64)(timing.vActive + timing.vBlank) *
|
(UINT64)(timing.vActive + timing.vBlank) *
|
||||||
(UINT64)mode.refresh;
|
(UINT64)mode.refreshMilliHz;
|
||||||
const UINT64 pixelClock10KHz = (pixelClock + 5000) / 10000;
|
timing.pixelClock = (pixelClockMilliHz + 500) / 1000;
|
||||||
timing.pixelClock = pixelClock10KHz * 10000;
|
|
||||||
return timing.pixelClock != 0;
|
return timing.pixelClock != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +411,7 @@ static bool MakeDetailedTiming(
|
|||||||
timing.hBlank > 4095 || timing.vBlank > 4095)
|
timing.hBlank > 4095 || timing.vBlank > 4095)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const UINT64 pixelClock10KHz = timing.pixelClock / 10000;
|
const UINT64 pixelClock10KHz = (timing.pixelClock + 5000) / 10000;
|
||||||
if (pixelClock10KHz == 0 || pixelClock10KHz > 0xffff)
|
if (pixelClock10KHz == 0 || pixelClock10KHz > 0xffff)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -185,20 +185,20 @@ bool CIndirectDeviceContext::PopulateDefaultModes()
|
|||||||
if (!GetResolutionMemoryRequirements(configuredMode.width,
|
if (!GetResolutionMemoryRequirements(configuredMode.width,
|
||||||
configuredMode.height, alignment, frameSize, requiredIVSHMEMSize))
|
configuredMode.height, alignment, frameSize, requiredIVSHMEMSize))
|
||||||
{
|
{
|
||||||
DEBUG_WARN("Filtering invalid %s mode %ux%u@%u",
|
DEBUG_WARN("Filtering invalid %s mode %ux%u@%.3f",
|
||||||
configuredMode.extraMode ? "extra" : "configured",
|
configuredMode.extraMode ? "extra" : "configured",
|
||||||
configuredMode.width, configuredMode.height,
|
configuredMode.width, configuredMode.height,
|
||||||
configuredMode.refresh);
|
configuredMode.refreshMilliHz / 1000.0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requiredIVSHMEMSize > m_ivshmem.GetSize())
|
if (requiredIVSHMEMSize > m_ivshmem.GetSize())
|
||||||
{
|
{
|
||||||
DEBUG_WARN(
|
DEBUG_WARN(
|
||||||
"Filtering %s mode %ux%u@%u: requires %llu bytes of IVSHMEM, only %llu bytes are available",
|
"Filtering %s mode %ux%u@%.3f: requires %llu bytes of IVSHMEM, only %llu bytes are available",
|
||||||
configuredMode.extraMode ? "extra" : "configured",
|
configuredMode.extraMode ? "extra" : "configured",
|
||||||
configuredMode.width, configuredMode.height,
|
configuredMode.width, configuredMode.height,
|
||||||
configuredMode.refresh,
|
configuredMode.refreshMilliHz / 1000.0,
|
||||||
(unsigned long long)requiredIVSHMEMSize,
|
(unsigned long long)requiredIVSHMEMSize,
|
||||||
(unsigned long long)m_ivshmem.GetSize());
|
(unsigned long long)m_ivshmem.GetSize());
|
||||||
continue;
|
continue;
|
||||||
@@ -644,10 +644,11 @@ void CIndirectDeviceContext::ReloadSettings()
|
|||||||
CSettings::DisplayMode extraMode;
|
CSettings::DisplayMode extraMode;
|
||||||
if (g_settings.GetExtraMode(extraMode))
|
if (g_settings.GetExtraMode(extraMode))
|
||||||
{
|
{
|
||||||
const unsigned refresh = g_settings.GetDefaultRefresh();
|
const unsigned refreshMilliHz =
|
||||||
if (extraMode.refresh != refresh)
|
g_settings.GetDefaultRefreshMilliHz();
|
||||||
|
if (extraMode.refreshMilliHz != refreshMilliHz)
|
||||||
{
|
{
|
||||||
extraMode.refresh = refresh;
|
extraMode.refreshMilliHz = refreshMilliHz;
|
||||||
if (!g_settings.SetExtraMode(extraMode))
|
if (!g_settings.SetExtraMode(extraMode))
|
||||||
{
|
{
|
||||||
ReleaseSRWLockExclusive(&m_modeReloadLock);
|
ReleaseSRWLockExclusive(&m_modeReloadLock);
|
||||||
@@ -743,7 +744,38 @@ void CIndirectDeviceContext::OnSwapChainReady()
|
|||||||
if (replug)
|
if (replug)
|
||||||
m_replugQueued.store(1);
|
m_replugQueued.store(1);
|
||||||
else if (doSetMode)
|
else if (doSetMode)
|
||||||
g_pipe.SetDisplayMode(mode.width, mode.height, mode.refresh);
|
g_pipe.SetDisplayMode(
|
||||||
|
mode.width, mode.height, mode.refreshMilliHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT64 GreatestCommonDivisor(UINT64 a, UINT64 b)
|
||||||
|
{
|
||||||
|
while (b)
|
||||||
|
{
|
||||||
|
const UINT64 remainder = a % b;
|
||||||
|
a = b;
|
||||||
|
b = remainder;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetSignalRate(DISPLAYCONFIG_RATIONAL& rate,
|
||||||
|
UINT64 numerator, UINT32 denominator)
|
||||||
|
{
|
||||||
|
const UINT64 divisor = GreatestCommonDivisor(numerator, denominator);
|
||||||
|
numerator /= divisor;
|
||||||
|
denominator /= (UINT32)divisor;
|
||||||
|
|
||||||
|
if (numerator <= UINT32_MAX)
|
||||||
|
{
|
||||||
|
rate.Numerator = (UINT32)numerator;
|
||||||
|
rate.Denominator = denominator;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rate.Numerator =
|
||||||
|
(UINT32)((numerator + denominator / 2) / denominator);
|
||||||
|
rate.Denominator = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& signal,
|
static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& signal,
|
||||||
@@ -761,10 +793,9 @@ static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& signal,
|
|||||||
signal.AdditionalSignalInfo.vSyncFreqDivider = monitorMode ? 0 : 1;
|
signal.AdditionalSignalInfo.vSyncFreqDivider = monitorMode ? 0 : 1;
|
||||||
signal.AdditionalSignalInfo.videoStandard = 255;
|
signal.AdditionalSignalInfo.videoStandard = 255;
|
||||||
|
|
||||||
signal.vSyncFreq.Numerator = mode.refresh;
|
SetSignalRate(signal.vSyncFreq, mode.refreshMilliHz, 1000);
|
||||||
signal.vSyncFreq.Denominator = 1;
|
SetSignalRate(signal.hSyncFreq,
|
||||||
signal.hSyncFreq.Numerator = mode.refresh * signal.totalSize.cy;
|
(UINT64)mode.refreshMilliHz * signal.totalSize.cy, 1000);
|
||||||
signal.hSyncFreq.Denominator = 1;
|
|
||||||
|
|
||||||
signal.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
|
signal.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
|
||||||
signal.pixelRate = timing.pixelClock;
|
signal.pixelRate = timing.pixelClock;
|
||||||
@@ -964,10 +995,10 @@ void CIndirectDeviceContext::SetResolution(uint32_t width, uint32_t height)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CSettings::DisplayMode mode = {};
|
CSettings::DisplayMode mode = {};
|
||||||
mode.width = width;
|
mode.width = width;
|
||||||
mode.height = height;
|
mode.height = height;
|
||||||
mode.refresh = g_settings.GetDefaultRefresh();
|
mode.refreshMilliHz = g_settings.GetDefaultRefreshMilliHz();
|
||||||
mode.preferred = true;
|
mode.preferred = true;
|
||||||
|
|
||||||
bool modesLoaded = false;
|
bool modesLoaded = false;
|
||||||
AcquireSRWLockExclusive(&m_modeReloadLock);
|
AcquireSRWLockExclusive(&m_modeReloadLock);
|
||||||
|
|||||||
@@ -287,14 +287,15 @@ void CPipeServer::SetCursorPos(uint32_t x, uint32_t y)
|
|||||||
WriteMsg(msg);
|
WriteMsg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPipeServer::SetDisplayMode(uint32_t width, uint32_t height, uint32_t refresh)
|
void CPipeServer::SetDisplayMode(
|
||||||
|
uint32_t width, uint32_t height, uint32_t refreshMilliHz)
|
||||||
{
|
{
|
||||||
LGPipeMsg msg = {};
|
LGPipeMsg msg = {};
|
||||||
msg.size = sizeof(msg);
|
msg.size = sizeof(msg);
|
||||||
msg.type = LGPipeMsg::SETDISPLAYMODE;
|
msg.type = LGPipeMsg::SETDISPLAYMODE;
|
||||||
msg.displayMode.width = width;
|
msg.displayMode.width = width;
|
||||||
msg.displayMode.height = height;
|
msg.displayMode.height = height;
|
||||||
msg.displayMode.refresh = refresh;
|
msg.displayMode.refreshMilliHz = refreshMilliHz;
|
||||||
WriteMsg(msg);
|
WriteMsg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ class CPipeServer
|
|||||||
void SetDeviceContext(CIndirectDeviceContext* context);
|
void SetDeviceContext(CIndirectDeviceContext* context);
|
||||||
|
|
||||||
void SetCursorPos(uint32_t x, uint32_t y);
|
void SetCursorPos(uint32_t x, uint32_t y);
|
||||||
void SetDisplayMode(uint32_t width, uint32_t height, uint32_t refresh);
|
void SetDisplayMode(
|
||||||
|
uint32_t width, uint32_t height, uint32_t refreshMilliHz);
|
||||||
void SetGPUStatus(bool software);
|
void SetGPUStatus(bool software);
|
||||||
void ResolutionRejected(uint32_t width, uint32_t height,
|
void ResolutionRejected(uint32_t width, uint32_t height,
|
||||||
uint32_t requiredSizeMiB);
|
uint32_t requiredSizeMiB);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "CSettings.h"
|
#include "CSettings.h"
|
||||||
#include "CDebug.h"
|
#include "CDebug.h"
|
||||||
#include "DefaultDisplayModes.h"
|
#include "DefaultDisplayModes.h"
|
||||||
|
#include "RefreshRate.h"
|
||||||
|
|
||||||
#include <wdf.h>
|
#include <wdf.h>
|
||||||
|
|
||||||
@@ -34,14 +35,16 @@ CSettings::CSettings()
|
|||||||
|
|
||||||
CSettings::DisplayModes CSettings::LoadModes()
|
CSettings::DisplayModes CSettings::LoadModes()
|
||||||
{
|
{
|
||||||
const unsigned defaultRefresh = GetDefaultRefresh();
|
const unsigned defaultRefreshMilliHz = GetDefaultRefreshMilliHz();
|
||||||
DisplayModes displayModes;
|
DisplayModes displayModes;
|
||||||
|
|
||||||
bool hasPreferred = false;
|
bool hasPreferred = false;
|
||||||
DisplayMode m;
|
DisplayMode m;
|
||||||
if (GetExtraMode(m))
|
if (GetExtraMode(m))
|
||||||
{
|
{
|
||||||
DEBUG_INFO("ExtraMode: %ux%u@%u%s", m.width, m.height, m.refresh, m.preferred ? "*" : "");
|
const std::wstring refresh = LGFormatRefreshRate(m.refreshMilliHz);
|
||||||
|
DEBUG_INFO("ExtraMode: %ux%u@%ls%s", m.width, m.height,
|
||||||
|
refresh.c_str(), m.preferred ? "*" : "");
|
||||||
displayModes.push_back(m);
|
displayModes.push_back(m);
|
||||||
hasPreferred = m.preferred;
|
hasPreferred = m.preferred;
|
||||||
}
|
}
|
||||||
@@ -54,11 +57,12 @@ CSettings::DisplayModes CSettings::LoadModes()
|
|||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
|
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
|
||||||
{
|
{
|
||||||
m.width = DefaultDisplayModes[i][0];
|
m.width = DefaultDisplayModes[i][0];
|
||||||
m.height = DefaultDisplayModes[i][1];
|
m.height = DefaultDisplayModes[i][1];
|
||||||
m.refresh = defaultRefresh;
|
m.refreshMilliHz = defaultRefreshMilliHz;
|
||||||
m.preferred = !hasPreferred && (i == DefaultPreferredDisplayMode);
|
m.preferred = !hasPreferred &&
|
||||||
m.extraMode = false;
|
(i == DefaultPreferredDisplayMode);
|
||||||
|
m.extraMode = false;
|
||||||
displayModes.push_back(m);
|
displayModes.push_back(m);
|
||||||
}
|
}
|
||||||
return displayModes;
|
return displayModes;
|
||||||
@@ -79,8 +83,9 @@ CSettings::DisplayModes CSettings::LoadModes()
|
|||||||
bool CSettings::SetExtraMode(const DisplayMode& mode)
|
bool CSettings::SetExtraMode(const DisplayMode& mode)
|
||||||
{
|
{
|
||||||
WCHAR buf[64];
|
WCHAR buf[64];
|
||||||
_snwprintf_s(buf, _countof(buf), _TRUNCATE, L"%ux%u@%u%s",
|
const std::wstring refresh = LGFormatRefreshRate(mode.refreshMilliHz);
|
||||||
mode.width, mode.height, mode.refresh,
|
_snwprintf_s(buf, _countof(buf), _TRUNCATE, L"%ux%u@%ls%s",
|
||||||
|
mode.width, mode.height, refresh.c_str(),
|
||||||
mode.preferred ? L"*" : L"");
|
mode.preferred ? L"*" : L"");
|
||||||
|
|
||||||
HKEY hKey = NULL;
|
HKEY hKey = NULL;
|
||||||
@@ -226,24 +231,45 @@ bool CSettings::GetExtraMode(DisplayMode& mode)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned CSettings::GetDefaultRefresh() const
|
unsigned CSettings::GetDefaultRefreshMilliHz() const
|
||||||
{
|
{
|
||||||
DWORD refresh = 60;
|
HKEY hKey = nullptr;
|
||||||
DWORD cb = sizeof(refresh);
|
LONG status = RegOpenKeyExW(
|
||||||
HKEY hKey = nullptr;
|
HKEY_LOCAL_MACHINE, LGIDD_REGKEY, 0, KEY_QUERY_VALUE, &hKey);
|
||||||
|
if (status != ERROR_SUCCESS)
|
||||||
|
return 60000;
|
||||||
|
|
||||||
LONG st = RegOpenKeyExW(HKEY_LOCAL_MACHINE, LGIDD_REGKEY, 0, KEY_QUERY_VALUE, &hKey);
|
DWORD type = 0;
|
||||||
if (st == ERROR_SUCCESS)
|
DWORD size = 0;
|
||||||
|
status = RegQueryValueExW(
|
||||||
|
hKey, L"DefaultRefresh", nullptr, &type, nullptr, &size);
|
||||||
|
if (status != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
DWORD type = 0;
|
|
||||||
st = RegGetValueW(hKey, nullptr, L"DefaultRefresh", RRF_RT_REG_DWORD, &type, &refresh, &cb);
|
|
||||||
RegCloseKey(hKey);
|
RegCloseKey(hKey);
|
||||||
|
return 60000;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st != ERROR_SUCCESS || refresh < 30 || refresh > 1000)
|
unsigned refreshMilliHz = 0;
|
||||||
return 60;
|
if (type == REG_DWORD && size == sizeof(DWORD))
|
||||||
|
{
|
||||||
|
DWORD refresh = 0;
|
||||||
|
status = RegQueryValueExW(hKey, L"DefaultRefresh", nullptr, &type,
|
||||||
|
(LPBYTE)&refresh, &size);
|
||||||
|
if (status == ERROR_SUCCESS && refresh >= 24 && refresh <= 1000)
|
||||||
|
refreshMilliHz = refresh * 1000;
|
||||||
|
}
|
||||||
|
else if ((type == REG_SZ || type == REG_EXPAND_SZ) && size &&
|
||||||
|
size % sizeof(wchar_t) == 0)
|
||||||
|
{
|
||||||
|
std::vector<wchar_t> value(size / sizeof(wchar_t) + 1, L'\0');
|
||||||
|
status = RegQueryValueExW(hKey, L"DefaultRefresh", nullptr, &type,
|
||||||
|
(LPBYTE)value.data(), &size);
|
||||||
|
if (status == ERROR_SUCCESS)
|
||||||
|
LGParseRefreshRate(value.data(), refreshMilliHz);
|
||||||
|
}
|
||||||
|
|
||||||
return refresh;
|
RegCloseKey(hKey);
|
||||||
|
return refreshMilliHz ? refreshMilliHz : 60000;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSettings::ReadModesValue(std::vector<std::wstring> &out) const
|
bool CSettings::ReadModesValue(std::vector<std::wstring> &out) const
|
||||||
@@ -320,16 +346,14 @@ bool CSettings::ParseModeString(const std::wstring& in, DisplayMode& out)
|
|||||||
|
|
||||||
if (!toUnsigned(s.substr(0, xPos), out.width) ||
|
if (!toUnsigned(s.substr(0, xPos), out.width) ||
|
||||||
!toUnsigned(s.substr(xPos + 1, atPos - (xPos + 1)), out.height) ||
|
!toUnsigned(s.substr(xPos + 1, atPos - (xPos + 1)), out.height) ||
|
||||||
!toUnsigned(s.substr(atPos + 1), out.refresh))
|
!LGParseRefreshRate(s.substr(atPos + 1), out.refreshMilliHz))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (out.width < 640 ||
|
if (out.width < 640 ||
|
||||||
out.height < 480 ||
|
out.height < 480 ||
|
||||||
out.width > 16384 ||
|
out.width > 16384 ||
|
||||||
out.height > 16384 ||
|
out.height > 16384)
|
||||||
out.refresh < 30 ||
|
|
||||||
out.refresh > 1000)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
out.extraMode = false;
|
out.extraMode = false;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class CSettings
|
|||||||
{
|
{
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
unsigned height;
|
||||||
unsigned refresh;
|
unsigned refreshMilliHz;
|
||||||
bool preferred;
|
bool preferred;
|
||||||
bool extraMode;
|
bool extraMode;
|
||||||
};
|
};
|
||||||
@@ -41,7 +41,7 @@ class CSettings
|
|||||||
DisplayModes LoadModes();
|
DisplayModes LoadModes();
|
||||||
bool SetExtraMode(const DisplayMode & mode);
|
bool SetExtraMode(const DisplayMode & mode);
|
||||||
bool GetExtraMode(DisplayMode & mode);
|
bool GetExtraMode(DisplayMode & mode);
|
||||||
unsigned GetDefaultRefresh() const;
|
unsigned GetDefaultRefreshMilliHz() const;
|
||||||
|
|
||||||
std::wstring ReadStringValue(const wchar_t* name, const wchar_t* defaultValue = nullptr);
|
std::wstring ReadStringValue(const wchar_t* name, const wchar_t* defaultValue = nullptr);
|
||||||
bool ReadBoolValue(const wchar_t* name, bool defaultValue = false);
|
bool ReadBoolValue(const wchar_t* name, bool defaultValue = false);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CConfigWindow.h"
|
#include "CConfigWindow.h"
|
||||||
|
#include "RefreshRate.h"
|
||||||
#include "CListBox.h"
|
#include "CListBox.h"
|
||||||
#include "CGroupBox.h"
|
#include "CGroupBox.h"
|
||||||
#include "CEditWidget.h"
|
#include "CEditWidget.h"
|
||||||
@@ -160,7 +161,7 @@ LRESULT CConfigWindow::onCreate()
|
|||||||
|
|
||||||
m_modeWidth.reset(new CEditWidget(WS_TABSTOP | ES_LEFT | ES_NUMBER, m_hwnd));
|
m_modeWidth.reset(new CEditWidget(WS_TABSTOP | ES_LEFT | ES_NUMBER, m_hwnd));
|
||||||
m_modeHeight.reset(new CEditWidget(WS_TABSTOP | ES_LEFT | ES_NUMBER, m_hwnd));
|
m_modeHeight.reset(new CEditWidget(WS_TABSTOP | ES_LEFT | ES_NUMBER, m_hwnd));
|
||||||
m_modeRefresh.reset(new CEditWidget(WS_TABSTOP | ES_LEFT | ES_NUMBER, m_hwnd));
|
m_modeRefresh.reset(new CEditWidget(WS_TABSTOP | ES_LEFT, m_hwnd));
|
||||||
m_modePreferred.reset(new CCheckbox(L"prefer", 0, m_hwnd));
|
m_modePreferred.reset(new CCheckbox(L"prefer", 0, m_hwnd));
|
||||||
|
|
||||||
m_modeUpdate.reset(new CButton(L"Update", WS_TABSTOP, m_hwnd));
|
m_modeUpdate.reset(new CButton(L"Update", WS_TABSTOP, m_hwnd));
|
||||||
@@ -173,11 +174,11 @@ LRESULT CConfigWindow::onCreate()
|
|||||||
m_modeRevert.reset(new CButton(L"Revert", WS_TABSTOP, m_hwnd));
|
m_modeRevert.reset(new CButton(L"Revert", WS_TABSTOP, m_hwnd));
|
||||||
|
|
||||||
m_defRefreshLabel.reset(new CStaticWidget(L"Default refresh:", SS_CENTERIMAGE, m_hwnd));
|
m_defRefreshLabel.reset(new CStaticWidget(L"Default refresh:", SS_CENTERIMAGE, m_hwnd));
|
||||||
m_defRefresh.reset(new CEditWidget(ES_LEFT | ES_NUMBER | WS_TABSTOP, m_hwnd));
|
m_defRefresh.reset(new CEditWidget(ES_LEFT | WS_TABSTOP, m_hwnd));
|
||||||
m_defRefreshHz.reset(new CStaticWidget(L"Hz", SS_CENTERIMAGE, m_hwnd));
|
m_defRefreshHz.reset(new CStaticWidget(L"Hz", SS_CENTERIMAGE, m_hwnd));
|
||||||
|
|
||||||
if (m_defaultRefresh)
|
if (m_defaultRefresh)
|
||||||
m_defRefresh->setNumericValue(*m_defaultRefresh);
|
m_defRefresh->setValue(LGFormatRefreshRate(*m_defaultRefresh));
|
||||||
else
|
else
|
||||||
m_defRefresh->disable();
|
m_defRefresh->disable();
|
||||||
|
|
||||||
@@ -253,7 +254,7 @@ void CConfigWindow::onModeListSelectChange()
|
|||||||
auto &mode = (*m_modes)[index];
|
auto &mode = (*m_modes)[index];
|
||||||
m_modeWidth->setNumericValue(mode.width);
|
m_modeWidth->setNumericValue(mode.width);
|
||||||
m_modeHeight->setNumericValue(mode.height);
|
m_modeHeight->setNumericValue(mode.height);
|
||||||
m_modeRefresh->setNumericValue(mode.refresh);
|
m_modeRefresh->setValue(LGFormatRefreshRate(mode.refreshMilliHz));
|
||||||
m_modePreferred->setChecked(mode.preferred);
|
m_modePreferred->setChecked(mode.preferred);
|
||||||
}
|
}
|
||||||
EnableWindow(*m_modeUpdate, TRUE);
|
EnableWindow(*m_modeUpdate, TRUE);
|
||||||
@@ -286,7 +287,6 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
|
|||||||
{
|
{
|
||||||
mode.width = m_modeWidth->getNumericValue();
|
mode.width = m_modeWidth->getNumericValue();
|
||||||
mode.height = m_modeHeight->getNumericValue();
|
mode.height = m_modeHeight->getNumericValue();
|
||||||
mode.refresh = m_modeRefresh->getNumericValue();
|
|
||||||
mode.preferred = m_modePreferred->isChecked();
|
mode.preferred = m_modePreferred->isChecked();
|
||||||
}
|
}
|
||||||
catch (std::logic_error&)
|
catch (std::logic_error&)
|
||||||
@@ -294,6 +294,12 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned refreshMilliHz;
|
||||||
|
if (!LGParseRefreshRate(
|
||||||
|
m_modeRefresh->getValue(), refreshMilliHz))
|
||||||
|
return 0;
|
||||||
|
mode.refreshMilliHz = refreshMilliHz;
|
||||||
|
|
||||||
m_modeBox->clear();
|
m_modeBox->clear();
|
||||||
m_modeBox->setSel(updateModeList(index));
|
m_modeBox->setSel(updateModeList(index));
|
||||||
}
|
}
|
||||||
@@ -319,14 +325,10 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
|
|||||||
}
|
}
|
||||||
else if (m_defRefresh && hwnd == *m_defRefresh && code == EN_CHANGE && m_defaultRefresh)
|
else if (m_defRefresh && hwnd == *m_defRefresh && code == EN_CHANGE && m_defaultRefresh)
|
||||||
{
|
{
|
||||||
try
|
unsigned refreshMilliHz;
|
||||||
{
|
if (!LGParseRefreshRate(m_defRefresh->getValue(), refreshMilliHz))
|
||||||
m_defaultRefresh = m_defRefresh->getNumericValue();
|
|
||||||
}
|
|
||||||
catch (std::logic_error &)
|
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
m_defaultRefresh = refreshMilliHz;
|
||||||
}
|
}
|
||||||
else if (m_prefNoGPU && hwnd == *m_prefNoGPU && code == BN_CLICKED && m_noGPU)
|
else if (m_prefNoGPU && hwnd == *m_prefNoGPU && code == BN_CLICKED && m_noGPU)
|
||||||
{
|
{
|
||||||
@@ -377,7 +379,7 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_defaultRefresh)
|
if (m_defaultRefresh)
|
||||||
m_defRefresh->setNumericValue(*m_defaultRefresh);
|
m_defRefresh->setValue(LGFormatRefreshRate(*m_defaultRefresh));
|
||||||
else
|
else
|
||||||
m_defRefresh->disable();
|
m_defRefresh->disable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -514,8 +514,10 @@ void CPipeClient::HandleSetDisplayMode(const LGPipeMsg& msg)
|
|||||||
DEVMODE dm = displays[lgIndex].mode;
|
DEVMODE dm = displays[lgIndex].mode;
|
||||||
dm.dmPelsWidth = msg.displayMode.width;
|
dm.dmPelsWidth = msg.displayMode.width;
|
||||||
dm.dmPelsHeight = msg.displayMode.height;
|
dm.dmPelsHeight = msg.displayMode.height;
|
||||||
dm.dmDisplayFrequency = msg.displayMode.refresh;
|
dm.dmDisplayFrequency =
|
||||||
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
(msg.displayMode.refreshMilliHz + 500) / 1000;
|
||||||
|
dm.dmFields =
|
||||||
|
DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
||||||
|
|
||||||
LONG result = ChangeDisplaySettingsEx(displays[lgIndex].device.DeviceName,
|
LONG result = ChangeDisplaySettingsEx(displays[lgIndex].device.DeviceName,
|
||||||
&dm, NULL, CDS_UPDATEREGISTRY, NULL);
|
&dm, NULL, CDS_UPDATEREGISTRY, NULL);
|
||||||
|
|||||||
@@ -25,10 +25,11 @@
|
|||||||
#include <CDebug.h>
|
#include <CDebug.h>
|
||||||
|
|
||||||
#include "DefaultDisplayModes.h"
|
#include "DefaultDisplayModes.h"
|
||||||
|
#include "RefreshRate.h"
|
||||||
|
|
||||||
#define LGIDD_REGKEY L"SOFTWARE\\LookingGlass\\IDD"
|
#define LGIDD_REGKEY L"SOFTWARE\\LookingGlass\\IDD"
|
||||||
|
|
||||||
const DWORD DEFAULT_REFRESH = 120;
|
const DWORD DEFAULT_REFRESH = 120000;
|
||||||
|
|
||||||
CRegistrySettings::CRegistrySettings() : hKey(nullptr) {}
|
CRegistrySettings::CRegistrySettings() : hKey(nullptr) {}
|
||||||
|
|
||||||
@@ -60,7 +61,8 @@ static std::basic_string<T> trim(const std::basic_string<T> &s)
|
|||||||
return s.substr(b, e - b);
|
return s.substr(b, e - b);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::wregex displayMode(L"(\\d+)x(\\d+)@(\\d+)(\\*)?");
|
static std::wregex displayMode(
|
||||||
|
L"(\\d+)x(\\d+)@(\\d+(?:\\.\\d{1,3})?)(\\*)?");
|
||||||
|
|
||||||
static std::optional<DisplayMode> parseDisplayMode(const std::wstring &str)
|
static std::optional<DisplayMode> parseDisplayMode(const std::wstring &str)
|
||||||
{
|
{
|
||||||
@@ -71,9 +73,10 @@ static std::optional<DisplayMode> parseDisplayMode(const std::wstring &str)
|
|||||||
return {};
|
return {};
|
||||||
|
|
||||||
DisplayMode mode;
|
DisplayMode mode;
|
||||||
mode.width = std::stoul(match[1]);
|
mode.width = std::stoul(match[1]);
|
||||||
mode.height = std::stoul(match[2]);
|
mode.height = std::stoul(match[2]);
|
||||||
mode.refresh = std::stoul(match[3]);
|
if (!LGParseRefreshRate(match[3], mode.refreshMilliHz))
|
||||||
|
return {};
|
||||||
mode.preferred = match[4] == L"*";
|
mode.preferred = match[4] == L"*";
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
@@ -81,16 +84,17 @@ static std::optional<DisplayMode> parseDisplayMode(const std::wstring &str)
|
|||||||
std::vector<DisplayMode> CRegistrySettings::getDefaultModes()
|
std::vector<DisplayMode> CRegistrySettings::getDefaultModes()
|
||||||
{
|
{
|
||||||
auto defaultRefresh = getDefaultRefresh();
|
auto defaultRefresh = getDefaultRefresh();
|
||||||
int refresh = defaultRefresh ? *defaultRefresh : DEFAULT_REFRESH;
|
const unsigned refreshMilliHz =
|
||||||
|
defaultRefresh ? *defaultRefresh : DEFAULT_REFRESH;
|
||||||
|
|
||||||
std::vector<DisplayMode> result;
|
std::vector<DisplayMode> result;
|
||||||
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
|
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
|
||||||
{
|
{
|
||||||
DisplayMode mode;
|
DisplayMode mode;
|
||||||
mode.width = DefaultDisplayModes[i][0];
|
mode.width = DefaultDisplayModes[i][0];
|
||||||
mode.height = DefaultDisplayModes[i][1];
|
mode.height = DefaultDisplayModes[i][1];
|
||||||
mode.refresh = refresh;
|
mode.refreshMilliHz = refreshMilliHz;
|
||||||
mode.preferred = i == DefaultPreferredDisplayMode;
|
mode.preferred = i == DefaultPreferredDisplayMode;
|
||||||
result.emplace_back(mode);
|
result.emplace_back(mode);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -159,7 +163,7 @@ std::wstring DisplayMode::toString()
|
|||||||
serialized.push_back('x');
|
serialized.push_back('x');
|
||||||
serialized.append(std::to_wstring(height));
|
serialized.append(std::to_wstring(height));
|
||||||
serialized.push_back('@');
|
serialized.push_back('@');
|
||||||
serialized.append(std::to_wstring(refresh));
|
serialized.append(LGFormatRefreshRate(refreshMilliHz));
|
||||||
if (preferred)
|
if (preferred)
|
||||||
serialized.push_back('*');
|
serialized.push_back('*');
|
||||||
return serialized;
|
return serialized;
|
||||||
@@ -167,24 +171,50 @@ std::wstring DisplayMode::toString()
|
|||||||
|
|
||||||
std::optional<DWORD> CRegistrySettings::getDefaultRefresh()
|
std::optional<DWORD> CRegistrySettings::getDefaultRefresh()
|
||||||
{
|
{
|
||||||
DWORD result, cbData = sizeof result;
|
DWORD type = 0;
|
||||||
|
DWORD size = 0;
|
||||||
LSTATUS status = RegGetValue(hKey, nullptr, L"DefaultRefresh", RRF_RT_REG_DWORD, nullptr, &result, &cbData);
|
LSTATUS status = RegQueryValueExW(
|
||||||
switch (status)
|
hKey, L"DefaultRefresh", nullptr, &type, nullptr, &size);
|
||||||
{
|
if (status == ERROR_FILE_NOT_FOUND)
|
||||||
case ERROR_SUCCESS:
|
|
||||||
return result;
|
|
||||||
case ERROR_FILE_NOT_FOUND:
|
|
||||||
return DEFAULT_REFRESH;
|
return DEFAULT_REFRESH;
|
||||||
default:
|
if (status != ERROR_SUCCESS)
|
||||||
DEBUG_ERROR_HR(status, "RegGetValue(Modes)");
|
{
|
||||||
|
DEBUG_ERROR_HR(status, "RegQueryValueEx(DefaultRefresh)");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == REG_DWORD && size == sizeof(DWORD))
|
||||||
|
{
|
||||||
|
DWORD refresh = 0;
|
||||||
|
status = RegQueryValueExW(hKey, L"DefaultRefresh", nullptr, &type,
|
||||||
|
(LPBYTE)&refresh, &size);
|
||||||
|
if (status == ERROR_SUCCESS && refresh >= 24 && refresh <= 1000)
|
||||||
|
return refresh * 1000;
|
||||||
|
}
|
||||||
|
else if ((type == REG_SZ || type == REG_EXPAND_SZ) && size &&
|
||||||
|
size % sizeof(wchar_t) == 0)
|
||||||
|
{
|
||||||
|
std::vector<wchar_t> value(size / sizeof(wchar_t) + 1, L'\0');
|
||||||
|
status = RegQueryValueExW(hKey, L"DefaultRefresh", nullptr, &type,
|
||||||
|
(LPBYTE)value.data(), &size);
|
||||||
|
if (status == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
unsigned refreshMilliHz;
|
||||||
|
if (LGParseRefreshRate(value.data(), refreshMilliHz))
|
||||||
|
return refreshMilliHz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_ERROR("Invalid DefaultRefresh value");
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
LSTATUS CRegistrySettings::setDefaultRefresh(DWORD refresh)
|
LSTATUS CRegistrySettings::setDefaultRefresh(DWORD refreshMilliHz)
|
||||||
{
|
{
|
||||||
return RegSetValueEx(hKey, L"DefaultRefresh", 0, REG_DWORD, (LPBYTE) &refresh, sizeof(DWORD));
|
const std::wstring value = LGFormatRefreshRate(refreshMilliHz);
|
||||||
|
return RegSetValueExW(hKey, L"DefaultRefresh", 0, REG_SZ,
|
||||||
|
(const BYTE *)value.c_str(),
|
||||||
|
(DWORD)((value.size() + 1) * sizeof(wchar_t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<bool> CRegistrySettings::getNoGPU()
|
std::optional<bool> CRegistrySettings::getNoGPU()
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
struct DisplayMode {
|
struct DisplayMode {
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
unsigned height;
|
||||||
unsigned refresh;
|
unsigned refreshMilliHz;
|
||||||
bool preferred;
|
bool preferred;
|
||||||
|
|
||||||
std::wstring toString();
|
std::wstring toString();
|
||||||
@@ -49,7 +49,7 @@ public:
|
|||||||
LSTATUS setModes(const std::vector<DisplayMode> &modes);
|
LSTATUS setModes(const std::vector<DisplayMode> &modes);
|
||||||
|
|
||||||
std::optional<DWORD> getDefaultRefresh();
|
std::optional<DWORD> getDefaultRefresh();
|
||||||
LSTATUS setDefaultRefresh(DWORD refresh);
|
LSTATUS setDefaultRefresh(DWORD refreshMilliHz);
|
||||||
|
|
||||||
std::optional<bool> getNoGPU();
|
std::optional<bool> getNoGPU();
|
||||||
LSTATUS setNoGPU(bool noGPU);
|
LSTATUS setNoGPU(bool noGPU);
|
||||||
|
|||||||
Reference in New Issue
Block a user