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:
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "CConfigWindow.h"
|
||||
#include "RefreshRate.h"
|
||||
#include "CListBox.h"
|
||||
#include "CGroupBox.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_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_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_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));
|
||||
|
||||
if (m_defaultRefresh)
|
||||
m_defRefresh->setNumericValue(*m_defaultRefresh);
|
||||
m_defRefresh->setValue(LGFormatRefreshRate(*m_defaultRefresh));
|
||||
else
|
||||
m_defRefresh->disable();
|
||||
|
||||
@@ -253,7 +254,7 @@ void CConfigWindow::onModeListSelectChange()
|
||||
auto &mode = (*m_modes)[index];
|
||||
m_modeWidth->setNumericValue(mode.width);
|
||||
m_modeHeight->setNumericValue(mode.height);
|
||||
m_modeRefresh->setNumericValue(mode.refresh);
|
||||
m_modeRefresh->setValue(LGFormatRefreshRate(mode.refreshMilliHz));
|
||||
m_modePreferred->setChecked(mode.preferred);
|
||||
}
|
||||
EnableWindow(*m_modeUpdate, TRUE);
|
||||
@@ -286,7 +287,6 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
|
||||
{
|
||||
mode.width = m_modeWidth->getNumericValue();
|
||||
mode.height = m_modeHeight->getNumericValue();
|
||||
mode.refresh = m_modeRefresh->getNumericValue();
|
||||
mode.preferred = m_modePreferred->isChecked();
|
||||
}
|
||||
catch (std::logic_error&)
|
||||
@@ -294,6 +294,12 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned refreshMilliHz;
|
||||
if (!LGParseRefreshRate(
|
||||
m_modeRefresh->getValue(), refreshMilliHz))
|
||||
return 0;
|
||||
mode.refreshMilliHz = refreshMilliHz;
|
||||
|
||||
m_modeBox->clear();
|
||||
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)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_defaultRefresh = m_defRefresh->getNumericValue();
|
||||
}
|
||||
catch (std::logic_error &)
|
||||
{
|
||||
unsigned refreshMilliHz;
|
||||
if (!LGParseRefreshRate(m_defRefresh->getValue(), refreshMilliHz))
|
||||
return 0;
|
||||
}
|
||||
m_defaultRefresh = refreshMilliHz;
|
||||
}
|
||||
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)
|
||||
m_defRefresh->setNumericValue(*m_defaultRefresh);
|
||||
m_defRefresh->setValue(LGFormatRefreshRate(*m_defaultRefresh));
|
||||
else
|
||||
m_defRefresh->disable();
|
||||
}
|
||||
|
||||
@@ -514,8 +514,10 @@ void CPipeClient::HandleSetDisplayMode(const LGPipeMsg& msg)
|
||||
DEVMODE dm = displays[lgIndex].mode;
|
||||
dm.dmPelsWidth = msg.displayMode.width;
|
||||
dm.dmPelsHeight = msg.displayMode.height;
|
||||
dm.dmDisplayFrequency = msg.displayMode.refresh;
|
||||
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
||||
dm.dmDisplayFrequency =
|
||||
(msg.displayMode.refreshMilliHz + 500) / 1000;
|
||||
dm.dmFields =
|
||||
DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
||||
|
||||
LONG result = ChangeDisplaySettingsEx(displays[lgIndex].device.DeviceName,
|
||||
&dm, NULL, CDS_UPDATEREGISTRY, NULL);
|
||||
|
||||
@@ -25,10 +25,11 @@
|
||||
#include <CDebug.h>
|
||||
|
||||
#include "DefaultDisplayModes.h"
|
||||
#include "RefreshRate.h"
|
||||
|
||||
#define LGIDD_REGKEY L"SOFTWARE\\LookingGlass\\IDD"
|
||||
|
||||
const DWORD DEFAULT_REFRESH = 120;
|
||||
const DWORD DEFAULT_REFRESH = 120000;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -71,9 +73,10 @@ static std::optional<DisplayMode> parseDisplayMode(const std::wstring &str)
|
||||
return {};
|
||||
|
||||
DisplayMode mode;
|
||||
mode.width = std::stoul(match[1]);
|
||||
mode.width = std::stoul(match[1]);
|
||||
mode.height = std::stoul(match[2]);
|
||||
mode.refresh = std::stoul(match[3]);
|
||||
if (!LGParseRefreshRate(match[3], mode.refreshMilliHz))
|
||||
return {};
|
||||
mode.preferred = match[4] == L"*";
|
||||
return mode;
|
||||
}
|
||||
@@ -81,16 +84,17 @@ static std::optional<DisplayMode> parseDisplayMode(const std::wstring &str)
|
||||
std::vector<DisplayMode> CRegistrySettings::getDefaultModes()
|
||||
{
|
||||
auto defaultRefresh = getDefaultRefresh();
|
||||
int refresh = defaultRefresh ? *defaultRefresh : DEFAULT_REFRESH;
|
||||
const unsigned refreshMilliHz =
|
||||
defaultRefresh ? *defaultRefresh : DEFAULT_REFRESH;
|
||||
|
||||
std::vector<DisplayMode> result;
|
||||
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
|
||||
{
|
||||
DisplayMode mode;
|
||||
mode.width = DefaultDisplayModes[i][0];
|
||||
mode.height = DefaultDisplayModes[i][1];
|
||||
mode.refresh = refresh;
|
||||
mode.preferred = i == DefaultPreferredDisplayMode;
|
||||
mode.width = DefaultDisplayModes[i][0];
|
||||
mode.height = DefaultDisplayModes[i][1];
|
||||
mode.refreshMilliHz = refreshMilliHz;
|
||||
mode.preferred = i == DefaultPreferredDisplayMode;
|
||||
result.emplace_back(mode);
|
||||
}
|
||||
return result;
|
||||
@@ -159,7 +163,7 @@ std::wstring DisplayMode::toString()
|
||||
serialized.push_back('x');
|
||||
serialized.append(std::to_wstring(height));
|
||||
serialized.push_back('@');
|
||||
serialized.append(std::to_wstring(refresh));
|
||||
serialized.append(LGFormatRefreshRate(refreshMilliHz));
|
||||
if (preferred)
|
||||
serialized.push_back('*');
|
||||
return serialized;
|
||||
@@ -167,24 +171,50 @@ std::wstring DisplayMode::toString()
|
||||
|
||||
std::optional<DWORD> CRegistrySettings::getDefaultRefresh()
|
||||
{
|
||||
DWORD result, cbData = sizeof result;
|
||||
|
||||
LSTATUS status = RegGetValue(hKey, nullptr, L"DefaultRefresh", RRF_RT_REG_DWORD, nullptr, &result, &cbData);
|
||||
switch (status)
|
||||
{
|
||||
case ERROR_SUCCESS:
|
||||
return result;
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
DWORD type = 0;
|
||||
DWORD size = 0;
|
||||
LSTATUS status = RegQueryValueExW(
|
||||
hKey, L"DefaultRefresh", nullptr, &type, nullptr, &size);
|
||||
if (status == ERROR_FILE_NOT_FOUND)
|
||||
return DEFAULT_REFRESH;
|
||||
default:
|
||||
DEBUG_ERROR_HR(status, "RegGetValue(Modes)");
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
DEBUG_ERROR_HR(status, "RegQueryValueEx(DefaultRefresh)");
|
||||
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()
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
struct DisplayMode {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh;
|
||||
unsigned refreshMilliHz;
|
||||
bool preferred;
|
||||
|
||||
std::wstring toString();
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
LSTATUS setModes(const std::vector<DisplayMode> &modes);
|
||||
|
||||
std::optional<DWORD> getDefaultRefresh();
|
||||
LSTATUS setDefaultRefresh(DWORD refresh);
|
||||
LSTATUS setDefaultRefresh(DWORD refreshMilliHz);
|
||||
|
||||
std::optional<bool> getNoGPU();
|
||||
LSTATUS setNoGPU(bool noGPU);
|
||||
|
||||
Reference in New Issue
Block a user