summaryrefslogtreecommitdiffstats
path: root/xbmc/settings/DisplaySettings.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/settings/DisplaySettings.cpp
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/settings/DisplaySettings.cpp')
-rw-r--r--xbmc/settings/DisplaySettings.cpp948
1 files changed, 948 insertions, 0 deletions
diff --git a/xbmc/settings/DisplaySettings.cpp b/xbmc/settings/DisplaySettings.cpp
new file mode 100644
index 0000000..25f230a
--- /dev/null
+++ b/xbmc/settings/DisplaySettings.cpp
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DisplaySettings.h"
+
+#include "ServiceBroker.h"
+#include "cores/VideoPlayer/VideoRenderers/ColorManager.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/LocalizeStrings.h"
+#include "guilib/StereoscopicsManager.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "rendering/RenderSystem.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WinSystem.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <float.h>
+#include <mutex>
+#include <string>
+#include <utility>
+#include <vector>
+
+#ifdef TARGET_WINDOWS
+#include "rendering/dx/DeviceResources.h"
+#endif
+
+using namespace KODI::MESSAGING;
+
+using KODI::MESSAGING::HELPERS::DialogResponse;
+
+// 0.1 second increments
+#define MAX_REFRESH_CHANGE_DELAY 200
+
+static RESOLUTION_INFO EmptyResolution;
+static RESOLUTION_INFO EmptyModifiableResolution;
+
+float square_error(float x, float y)
+{
+ float yonx = (x > 0) ? y / x : 0;
+ float xony = (y > 0) ? x / y : 0;
+ return std::max(yonx, xony);
+}
+
+static std::string ModeFlagsToString(unsigned int flags, bool identifier)
+{
+ std::string res;
+ if(flags & D3DPRESENTFLAG_INTERLACED)
+ res += "i";
+ else
+ res += "p";
+
+ if(!identifier)
+ res += " ";
+
+ if(flags & D3DPRESENTFLAG_MODE3DSBS)
+ res += "sbs";
+ else if(flags & D3DPRESENTFLAG_MODE3DTB)
+ res += "tab";
+ else if(identifier)
+ res += "std";
+ return res;
+}
+
+CDisplaySettings::CDisplaySettings()
+{
+ m_resolutions.resize(RES_CUSTOM);
+
+ m_zoomAmount = 1.0f;
+ m_pixelRatio = 1.0f;
+ m_verticalShift = 0.0f;
+ m_nonLinearStretched = false;
+ m_resolutionChangeAborted = false;
+}
+
+CDisplaySettings::~CDisplaySettings() = default;
+
+CDisplaySettings& CDisplaySettings::GetInstance()
+{
+ static CDisplaySettings sDisplaySettings;
+ return sDisplaySettings;
+}
+
+bool CDisplaySettings::Load(const TiXmlNode *settings)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_calibrations.clear();
+
+ if (settings == NULL)
+ return false;
+
+ const TiXmlElement *pElement = settings->FirstChildElement("resolutions");
+ if (!pElement)
+ {
+ CLog::Log(LOGERROR, "CDisplaySettings: settings file doesn't contain <resolutions>");
+ return false;
+ }
+
+ const TiXmlElement *pResolution = pElement->FirstChildElement("resolution");
+ while (pResolution)
+ {
+ // get the data for this calibration
+ RESOLUTION_INFO cal;
+
+ XMLUtils::GetString(pResolution, "description", cal.strMode);
+ XMLUtils::GetInt(pResolution, "subtitles", cal.iSubtitles);
+ XMLUtils::GetFloat(pResolution, "pixelratio", cal.fPixelRatio);
+#ifdef HAVE_X11
+ XMLUtils::GetFloat(pResolution, "refreshrate", cal.fRefreshRate);
+ XMLUtils::GetString(pResolution, "output", cal.strOutput);
+ XMLUtils::GetString(pResolution, "xrandrid", cal.strId);
+#endif
+
+ const TiXmlElement *pOverscan = pResolution->FirstChildElement("overscan");
+ if (pOverscan)
+ {
+ XMLUtils::GetInt(pOverscan, "left", cal.Overscan.left);
+ XMLUtils::GetInt(pOverscan, "top", cal.Overscan.top);
+ XMLUtils::GetInt(pOverscan, "right", cal.Overscan.right);
+ XMLUtils::GetInt(pOverscan, "bottom", cal.Overscan.bottom);
+ }
+
+ // mark calibration as not updated
+ // we must not delete those, resolution just might not be available
+ cal.iWidth = cal.iHeight = 0;
+
+ // store calibration, avoid adding duplicates
+ bool found = false;
+ for (ResolutionInfos::const_iterator it = m_calibrations.begin(); it != m_calibrations.end(); ++it)
+ {
+ if (StringUtils::EqualsNoCase(it->strMode, cal.strMode))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ m_calibrations.push_back(cal);
+
+ // iterate around
+ pResolution = pResolution->NextSiblingElement("resolution");
+ }
+
+ ApplyCalibrations();
+ return true;
+}
+
+bool CDisplaySettings::Save(TiXmlNode *settings) const
+{
+ if (settings == NULL)
+ return false;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ TiXmlElement xmlRootElement("resolutions");
+ TiXmlNode *pRoot = settings->InsertEndChild(xmlRootElement);
+ if (pRoot == NULL)
+ return false;
+
+ // save calibrations
+ for (ResolutionInfos::const_iterator it = m_calibrations.begin(); it != m_calibrations.end(); ++it)
+ {
+ // Write the resolution tag
+ TiXmlElement resElement("resolution");
+ TiXmlNode *pNode = pRoot->InsertEndChild(resElement);
+ if (pNode == NULL)
+ return false;
+
+ // Now write each of the pieces of information we need...
+ XMLUtils::SetString(pNode, "description", it->strMode);
+ XMLUtils::SetInt(pNode, "subtitles", it->iSubtitles);
+ XMLUtils::SetFloat(pNode, "pixelratio", it->fPixelRatio);
+#ifdef HAVE_X11
+ XMLUtils::SetFloat(pNode, "refreshrate", it->fRefreshRate);
+ XMLUtils::SetString(pNode, "output", it->strOutput);
+ XMLUtils::SetString(pNode, "xrandrid", it->strId);
+#endif
+
+ // create the overscan child
+ TiXmlElement overscanElement("overscan");
+ TiXmlNode *pOverscanNode = pNode->InsertEndChild(overscanElement);
+ if (pOverscanNode == NULL)
+ return false;
+
+ XMLUtils::SetInt(pOverscanNode, "left", it->Overscan.left);
+ XMLUtils::SetInt(pOverscanNode, "top", it->Overscan.top);
+ XMLUtils::SetInt(pOverscanNode, "right", it->Overscan.right);
+ XMLUtils::SetInt(pOverscanNode, "bottom", it->Overscan.bottom);
+ }
+
+ return true;
+}
+
+void CDisplaySettings::Clear()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_calibrations.clear();
+ m_resolutions.clear();
+ m_resolutions.resize(RES_CUSTOM);
+
+ m_zoomAmount = 1.0f;
+ m_pixelRatio = 1.0f;
+ m_verticalShift = 0.0f;
+ m_nonLinearStretched = false;
+}
+
+void CDisplaySettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == "videoscreen.cms3dlut")
+ {
+ std::string path = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ if (CGUIDialogFileBrowser::ShowAndGetFile(shares, ".3dlut", g_localizeStrings.Get(36580), path))
+ {
+ std::static_pointer_cast<CSettingString>(std::const_pointer_cast<CSetting>(setting))->SetValue(path);
+ }
+ }
+ else if (settingId == "videoscreen.displayprofile")
+ {
+ std::string path = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ if (CGUIDialogFileBrowser::ShowAndGetFile(shares, ".icc|.icm", g_localizeStrings.Get(36581), path))
+ {
+ std::static_pointer_cast<CSettingString>(std::const_pointer_cast<CSetting>(setting))->SetValue(path);
+ }
+ }
+}
+
+bool CDisplaySettings::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return false;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_RESOLUTION ||
+ settingId == CSettings::SETTING_VIDEOSCREEN_SCREEN)
+ {
+ RESOLUTION newRes = RES_DESKTOP;
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_RESOLUTION)
+ newRes = (RESOLUTION)std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREEN)
+ {
+ int screen = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+
+ // if triggered by a change of screenmode, screen may not have changed
+ if (screen == GetCurrentDisplayMode())
+ return true;
+
+ // get desktop resolution for screen
+ newRes = GetResolutionForScreen();
+ }
+
+ std::string screenmode = GetStringFromResolution(newRes);
+ if (!CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_VIDEOSCREEN_SCREENMODE, screenmode))
+ return false;
+ }
+
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREENMODE)
+ {
+ RESOLUTION oldRes = GetCurrentResolution();
+ RESOLUTION newRes = GetResolutionFromString(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
+
+ SetCurrentResolution(newRes, false);
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(newRes, false);
+
+ // check if the old or the new resolution was/is windowed
+ // in which case we don't show any prompt to the user
+ if (oldRes != RES_WINDOW && newRes != RES_WINDOW && oldRes != newRes)
+ {
+ if (!m_resolutionChangeAborted)
+ {
+ if (HELPERS::ShowYesNoDialogText(CVariant{13110}, CVariant{13111}, CVariant{""},
+ CVariant{""}, 15000) != DialogResponse::CHOICE_YES)
+ {
+ m_resolutionChangeAborted = true;
+ return false;
+ }
+ }
+ else
+ m_resolutionChangeAborted = false;
+ }
+ }
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_MONITOR)
+ {
+ CServiceBroker::GetWinSystem()->UpdateResolutions();
+ RESOLUTION newRes = GetResolutionForScreen();
+
+ SetCurrentResolution(newRes, false);
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(newRes, true);
+
+ if (!m_resolutionChangeAborted)
+ {
+ if (HELPERS::ShowYesNoDialogText(CVariant{13110}, CVariant{13111}, CVariant{""}, CVariant{""},
+ 10000) != DialogResponse::CHOICE_YES)
+ {
+ m_resolutionChangeAborted = true;
+ return false;
+ }
+ }
+ else
+ m_resolutionChangeAborted = false;
+
+ return true;
+ }
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_10BITSURFACES)
+ {
+#ifdef TARGET_WINDOWS
+ DX::DeviceResources::Get()->ApplyDisplaySettings();
+ return true;
+#endif
+ }
+#if defined(HAVE_X11) || defined(TARGET_WINDOWS_DESKTOP) || defined(TARGET_DARWIN_OSX)
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS)
+ {
+ auto winSystem = CServiceBroker::GetWinSystem();
+#if defined(HAVE_X11)
+ winSystem->UpdateResolutions();
+#elif defined(TARGET_WINDOWS_DESKTOP) || defined(TARGET_DARWIN_OSX)
+ CGraphicContext& gfxContext = winSystem->GetGfxContext();
+ gfxContext.SetVideoResolution(gfxContext.GetVideoResolution(), true);
+#endif
+ }
+#endif
+
+ return true;
+}
+
+bool CDisplaySettings::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+{
+ if (setting == NULL)
+ return false;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREENMODE)
+ {
+ std::shared_ptr<CSettingString> screenmodeSetting = std::static_pointer_cast<CSettingString>(setting);
+ std::string screenmode = screenmodeSetting->GetValue();
+ // in Eden there was no character ("i" or "p") indicating interlaced/progressive
+ // at the end so we just add a "p" and assume progressive
+ // no 3d mode existed before, so just assume std modes
+ if (screenmode.size() == 20)
+ return screenmodeSetting->SetValue(screenmode + "pstd");
+ if (screenmode.size() == 21)
+ return screenmodeSetting->SetValue(screenmode + "std");
+ }
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_PREFEREDSTEREOSCOPICMODE)
+ {
+ std::shared_ptr<CSettingInt> stereomodeSetting = std::static_pointer_cast<CSettingInt>(setting);
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ STEREOSCOPIC_PLAYBACK_MODE playbackMode = (STEREOSCOPIC_PLAYBACK_MODE) settings->GetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE);
+ if (stereomodeSetting->GetValue() == RENDER_STEREO_MODE_OFF)
+ {
+ // if preferred playback mode was OFF, update playback mode to ignore
+ if (playbackMode == STEREOSCOPIC_PLAYBACK_MODE_PREFERRED)
+ settings->SetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE, STEREOSCOPIC_PLAYBACK_MODE_IGNORE);
+ return stereomodeSetting->SetValue(RENDER_STEREO_MODE_AUTO);
+ }
+ else if (stereomodeSetting->GetValue() == RENDER_STEREO_MODE_MONO)
+ {
+ // if preferred playback mode was MONO, update playback mode
+ if (playbackMode == STEREOSCOPIC_PLAYBACK_MODE_PREFERRED)
+ settings->SetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE, STEREOSCOPIC_PLAYBACK_MODE_MONO);
+ return stereomodeSetting->SetValue(RENDER_STEREO_MODE_AUTO);
+ }
+ }
+
+ return false;
+}
+
+void CDisplaySettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (!setting)
+ return;
+
+ const std::string& settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_WHITELIST)
+ CResolutionUtils::PrintWhitelist();
+}
+
+void CDisplaySettings::SetMonitor(const std::string& monitor)
+{
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ const std::string curMonitor = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+ if (curMonitor != monitor)
+ {
+ m_resolutionChangeAborted = true;
+ settings->SetString(CSettings::SETTING_VIDEOSCREEN_MONITOR, monitor);
+ }
+}
+
+void CDisplaySettings::SetCurrentResolution(RESOLUTION resolution, bool save /* = false */)
+{
+ if (resolution == RES_WINDOW && !CServiceBroker::GetWinSystem()->CanDoWindowed())
+ resolution = RES_DESKTOP;
+
+ if (save)
+ {
+ // Save videoscreen.screenmode setting
+ std::string mode = GetStringFromResolution(resolution);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_VIDEOSCREEN_SCREENMODE, mode.c_str());
+
+ // Check if videoscreen.screen setting also needs to be saved
+ // e.g. if ToggleFullscreen is called
+ int currentDisplayMode = GetCurrentDisplayMode();
+ int currentDisplayModeSetting = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN);
+ if (currentDisplayMode != currentDisplayModeSetting)
+ {
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN, currentDisplayMode);
+ }
+ }
+ else if (resolution != m_currentResolution)
+ {
+ m_currentResolution = resolution;
+ SetChanged();
+ }
+}
+
+RESOLUTION CDisplaySettings::GetDisplayResolution() const
+{
+ return GetResolutionFromString(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_SCREENMODE));
+}
+
+const RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(size_t index) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (index >= m_resolutions.size())
+ return EmptyResolution;
+
+ return m_resolutions[index];
+}
+
+const RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(RESOLUTION resolution) const
+{
+ if (resolution <= RES_INVALID)
+ return EmptyResolution;
+
+ return GetResolutionInfo((size_t)resolution);
+}
+
+RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(size_t index)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (index >= m_resolutions.size())
+ {
+ EmptyModifiableResolution = RESOLUTION_INFO();
+ return EmptyModifiableResolution;
+ }
+
+ return m_resolutions[index];
+}
+
+RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(RESOLUTION resolution)
+{
+ if (resolution <= RES_INVALID)
+ {
+ EmptyModifiableResolution = RESOLUTION_INFO();
+ return EmptyModifiableResolution;
+ }
+
+ return GetResolutionInfo((size_t)resolution);
+}
+
+void CDisplaySettings::AddResolutionInfo(const RESOLUTION_INFO &resolution)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ RESOLUTION_INFO res(resolution);
+
+ if((res.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
+ {
+ /* add corrections for some special case modes frame packing modes */
+
+ if(res.iScreenWidth == 1920
+ && res.iScreenHeight == 2205)
+ {
+ res.iBlanking = 45;
+ res.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
+ }
+
+ if(res.iScreenWidth == 1280
+ && res.iScreenHeight == 1470)
+ {
+ res.iBlanking = 30;
+ res.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
+ }
+ }
+ m_resolutions.push_back(res);
+}
+
+void CDisplaySettings::ApplyCalibrations()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ // apply all calibrations to the resolutions
+ for (ResolutionInfos::const_iterator itCal = m_calibrations.begin(); itCal != m_calibrations.end(); ++itCal)
+ {
+ // find resolutions
+ for (size_t res = RES_DESKTOP; res < m_resolutions.size(); ++res)
+ {
+ if (StringUtils::EqualsNoCase(itCal->strMode, m_resolutions[res].strMode))
+ {
+ // overscan
+ m_resolutions[res].Overscan.left = itCal->Overscan.left;
+ if (m_resolutions[res].Overscan.left < -m_resolutions[res].iWidth/4)
+ m_resolutions[res].Overscan.left = -m_resolutions[res].iWidth/4;
+ if (m_resolutions[res].Overscan.left > m_resolutions[res].iWidth/4)
+ m_resolutions[res].Overscan.left = m_resolutions[res].iWidth/4;
+
+ m_resolutions[res].Overscan.top = itCal->Overscan.top;
+ if (m_resolutions[res].Overscan.top < -m_resolutions[res].iHeight/4)
+ m_resolutions[res].Overscan.top = -m_resolutions[res].iHeight/4;
+ if (m_resolutions[res].Overscan.top > m_resolutions[res].iHeight/4)
+ m_resolutions[res].Overscan.top = m_resolutions[res].iHeight/4;
+
+ m_resolutions[res].Overscan.right = itCal->Overscan.right;
+ if (m_resolutions[res].Overscan.right < m_resolutions[res].iWidth / 2)
+ m_resolutions[res].Overscan.right = m_resolutions[res].iWidth / 2;
+ if (m_resolutions[res].Overscan.right > m_resolutions[res].iWidth * 3/2)
+ m_resolutions[res].Overscan.right = m_resolutions[res].iWidth *3/2;
+
+ m_resolutions[res].Overscan.bottom = itCal->Overscan.bottom;
+ if (m_resolutions[res].Overscan.bottom < m_resolutions[res].iHeight / 2)
+ m_resolutions[res].Overscan.bottom = m_resolutions[res].iHeight / 2;
+ if (m_resolutions[res].Overscan.bottom > m_resolutions[res].iHeight * 3/2)
+ m_resolutions[res].Overscan.bottom = m_resolutions[res].iHeight * 3/2;
+
+ m_resolutions[res].iSubtitles = itCal->iSubtitles;
+ if (m_resolutions[res].iSubtitles < 0)
+ m_resolutions[res].iSubtitles = 0;
+ if (m_resolutions[res].iSubtitles > m_resolutions[res].iHeight * 3 / 2)
+ m_resolutions[res].iSubtitles = m_resolutions[res].iHeight * 3 / 2;
+
+ m_resolutions[res].fPixelRatio = itCal->fPixelRatio;
+ if (m_resolutions[res].fPixelRatio < 0.5f)
+ m_resolutions[res].fPixelRatio = 0.5f;
+ if (m_resolutions[res].fPixelRatio > 2.0f)
+ m_resolutions[res].fPixelRatio = 2.0f;
+ break;
+ }
+ }
+ }
+}
+
+void CDisplaySettings::UpdateCalibrations()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+ if (m_resolutions.size() <= RES_DESKTOP)
+ return;
+
+ // Add new (unique) resolutions
+ for (ResolutionInfos::const_iterator res(m_resolutions.cbegin() + RES_CUSTOM); res != m_resolutions.cend(); ++res)
+ if (std::find_if(m_calibrations.cbegin(), m_calibrations.cend(),
+ [&](const RESOLUTION_INFO& info) { return StringUtils::EqualsNoCase(res->strMode, info.strMode); }) == m_calibrations.cend())
+ m_calibrations.push_back(*res);
+
+ for (auto &cal : m_calibrations)
+ {
+ ResolutionInfos::const_iterator res(std::find_if(m_resolutions.cbegin() + RES_DESKTOP, m_resolutions.cend(),
+ [&](const RESOLUTION_INFO& info) { return StringUtils::EqualsNoCase(cal.strMode, info.strMode); }));
+
+ if (res != m_resolutions.cend())
+ {
+ //! @todo erase calibrations with default values
+ cal = *res;
+ }
+ }
+}
+
+void CDisplaySettings::ClearCalibrations()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_calibrations.clear();
+}
+
+DisplayMode CDisplaySettings::GetCurrentDisplayMode() const
+{
+ if (GetCurrentResolution() == RES_WINDOW)
+ return DM_WINDOWED;
+
+ return DM_FULLSCREEN;
+}
+
+RESOLUTION CDisplaySettings::FindBestMatchingResolution(const std::map<RESOLUTION, RESOLUTION_INFO> &resolutionInfos, int width, int height, float refreshrate, unsigned flags)
+{
+ // find the closest match to these in our res vector. If we have the screen, we score the res
+ RESOLUTION bestRes = RES_DESKTOP;
+ float bestScore = FLT_MAX;
+ flags &= D3DPRESENTFLAG_MODEMASK;
+
+ for (std::map<RESOLUTION, RESOLUTION_INFO>::const_iterator it = resolutionInfos.begin(); it != resolutionInfos.end(); ++it)
+ {
+ const RESOLUTION_INFO &info = it->second;
+
+ if ((info.dwFlags & D3DPRESENTFLAG_MODEMASK) != flags)
+ continue;
+
+ float score = 10 * (square_error((float)info.iScreenWidth, (float)width) +
+ square_error((float)info.iScreenHeight, (float)height) +
+ square_error(info.fRefreshRate, refreshrate));
+ if (score < bestScore)
+ {
+ bestScore = score;
+ bestRes = it->first;
+ }
+ }
+
+ return bestRes;
+}
+
+RESOLUTION CDisplaySettings::GetResolutionFromString(const std::string &strResolution)
+{
+
+ if (strResolution == "DESKTOP")
+ return RES_DESKTOP;
+ else if (strResolution == "WINDOW")
+ return RES_WINDOW;
+ else if (strResolution.size() >= 20)
+ {
+ // format: WWWWWHHHHHRRR.RRRRRP333, where W = width, H = height, R = refresh, P = interlace, 3 = stereo mode
+ int width = std::strtol(StringUtils::Mid(strResolution, 0,5).c_str(), NULL, 10);
+ int height = std::strtol(StringUtils::Mid(strResolution, 5,5).c_str(), NULL, 10);
+ float refresh = (float)std::strtod(StringUtils::Mid(strResolution, 10,9).c_str(), NULL);
+ unsigned flags = 0;
+
+ // look for 'i' and treat everything else as progressive,
+ if(StringUtils::Mid(strResolution, 19,1) == "i")
+ flags |= D3DPRESENTFLAG_INTERLACED;
+
+ if(StringUtils::Mid(strResolution, 20,3) == "sbs")
+ flags |= D3DPRESENTFLAG_MODE3DSBS;
+ else if(StringUtils::Mid(strResolution, 20,3) == "tab")
+ flags |= D3DPRESENTFLAG_MODE3DTB;
+
+ std::map<RESOLUTION, RESOLUTION_INFO> resolutionInfos;
+ for (size_t resolution = RES_DESKTOP; resolution < CDisplaySettings::GetInstance().ResolutionInfoSize(); resolution++)
+ resolutionInfos.insert(std::make_pair((RESOLUTION)resolution, CDisplaySettings::GetInstance().GetResolutionInfo(resolution)));
+
+ return FindBestMatchingResolution(resolutionInfos, width, height, refresh, flags);
+ }
+
+ return RES_DESKTOP;
+}
+
+std::string CDisplaySettings::GetStringFromResolution(RESOLUTION resolution, float refreshrate /* = 0.0f */)
+{
+ if (resolution == RES_WINDOW)
+ return "WINDOW";
+
+ if (resolution >= RES_DESKTOP && resolution < (RESOLUTION)CDisplaySettings::GetInstance().ResolutionInfoSize())
+ {
+ const RESOLUTION_INFO &info = CDisplaySettings::GetInstance().GetResolutionInfo(resolution);
+ // also handle RES_DESKTOP resolutions with non-default refresh rates
+ if (resolution != RES_DESKTOP || (refreshrate > 0.0f && refreshrate != info.fRefreshRate))
+ {
+ return StringUtils::Format("{:05}{:05}{:09.5f}{}", info.iScreenWidth, info.iScreenHeight,
+ refreshrate > 0.0f ? refreshrate : info.fRefreshRate,
+ ModeFlagsToString(info.dwFlags, true));
+ }
+ }
+
+ return "DESKTOP";
+}
+
+RESOLUTION CDisplaySettings::GetResolutionForScreen()
+{
+ DisplayMode mode = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN);
+ if (mode == DM_WINDOWED)
+ return RES_WINDOW;
+
+ return RES_DESKTOP;
+}
+
+static inline bool ModeSort(const StringSettingOption& i, const StringSettingOption& j)
+{
+ return (i.value > j.value);
+}
+
+void CDisplaySettings::SettingOptionsModesFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ for (auto index = (unsigned int)RES_CUSTOM; index < CDisplaySettings::GetInstance().ResolutionInfoSize(); ++index)
+ {
+ const auto mode = CDisplaySettings::GetInstance().GetResolutionInfo(index);
+
+ if (mode.dwFlags ^ D3DPRESENTFLAG_INTERLACED)
+ {
+ auto setting = GetStringFromResolution((RESOLUTION)index, mode.fRefreshRate);
+
+ list.emplace_back(
+ StringUtils::Format("{}x{}{} {:0.2f}Hz", mode.iScreenWidth, mode.iScreenHeight,
+ ModeFlagsToString(mode.dwFlags, false), mode.fRefreshRate),
+ setting);
+ }
+ }
+
+ std::sort(list.begin(), list.end(), ModeSort);
+}
+
+void CDisplaySettings::SettingOptionsRefreshChangeDelaysFiller(
+ const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(13551), 0);
+
+ for (int i = 1; i <= MAX_REFRESH_CHANGE_DELAY; i++)
+ list.emplace_back(
+ StringUtils::Format(g_localizeStrings.Get(13553), static_cast<double>(i) / 10.0), i);
+}
+
+void CDisplaySettings::SettingOptionsRefreshRatesFiller(const SettingConstPtr& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ // get the proper resolution
+ RESOLUTION res = CDisplaySettings::GetInstance().GetDisplayResolution();
+ if (res < RES_WINDOW)
+ return;
+
+ // only add "Windowed" if in windowed mode
+ if (res == RES_WINDOW)
+ {
+ current = "WINDOW";
+ list.emplace_back(g_localizeStrings.Get(242), current);
+ return;
+ }
+
+ RESOLUTION_INFO resInfo = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+ // The only meaningful parts of res here are iScreenWidth, iScreenHeight
+ std::vector<REFRESHRATE> refreshrates = CServiceBroker::GetWinSystem()->RefreshRates(resInfo.iScreenWidth, resInfo.iScreenHeight, resInfo.dwFlags);
+
+ bool match = false;
+ for (std::vector<REFRESHRATE>::const_iterator refreshrate = refreshrates.begin(); refreshrate != refreshrates.end(); ++refreshrate)
+ {
+ std::string screenmode = GetStringFromResolution((RESOLUTION)refreshrate->ResInfo_Index, refreshrate->RefreshRate);
+ if (!match && StringUtils::EqualsNoCase(std::static_pointer_cast<const CSettingString>(setting)->GetValue(), screenmode))
+ match = true;
+ list.emplace_back(StringUtils::Format("{:.2f}", refreshrate->RefreshRate), screenmode);
+ }
+
+ if (!match)
+ current = GetStringFromResolution(res, CServiceBroker::GetWinSystem()->DefaultRefreshRate(refreshrates).RefreshRate);
+}
+
+void CDisplaySettings::SettingOptionsResolutionsFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ RESOLUTION res = CDisplaySettings::GetInstance().GetDisplayResolution();
+ RESOLUTION_INFO info = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+ if (res == RES_WINDOW)
+ {
+ current = res;
+ list.emplace_back(g_localizeStrings.Get(242), res);
+ }
+ else
+ {
+ std::map<RESOLUTION, RESOLUTION_INFO> resolutionInfos;
+ std::vector<RESOLUTION_WHR> resolutions = CServiceBroker::GetWinSystem()->ScreenResolutions(info.fRefreshRate);
+ for (std::vector<RESOLUTION_WHR>::const_iterator resolution = resolutions.begin(); resolution != resolutions.end(); ++resolution)
+ {
+ list.emplace_back(StringUtils::Format("{}x{}{}", resolution->width, resolution->height,
+ ModeFlagsToString(resolution->flags, false)),
+ resolution->ResInfo_Index);
+
+ resolutionInfos.insert(std::make_pair((RESOLUTION)resolution->ResInfo_Index, CDisplaySettings::GetInstance().GetResolutionInfo(resolution->ResInfo_Index)));
+ }
+
+ current = FindBestMatchingResolution(resolutionInfos,
+ info.iScreenWidth, info.iScreenHeight,
+ info.fRefreshRate, info.dwFlags);
+ }
+}
+
+void CDisplaySettings::SettingOptionsDispModeFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ // The user should only be able to disable windowed modes with the canwindowed
+ // setting. When the user sets canwindowed to true but the windowing system
+ // does not support windowed modes, we would just shoot ourselves in the foot
+ // by offering the option.
+ if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_canWindowed && CServiceBroker::GetWinSystem()->CanDoWindowed())
+ list.emplace_back(g_localizeStrings.Get(242), DM_WINDOWED);
+
+ list.emplace_back(g_localizeStrings.Get(244), DM_FULLSCREEN);
+}
+
+void CDisplaySettings::SettingOptionsStereoscopicModesFiller(
+ const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ CGUIComponent *gui = CServiceBroker::GetGUI();
+ if (gui != nullptr)
+ {
+ const CStereoscopicsManager &stereoscopicsManager = gui->GetStereoscopicsManager();
+
+ for (int i = RENDER_STEREO_MODE_OFF; i < RENDER_STEREO_MODE_COUNT; i++)
+ {
+ RENDER_STEREO_MODE mode = (RENDER_STEREO_MODE) i;
+ if (CServiceBroker::GetRenderSystem()->SupportsStereo(mode))
+ list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(mode), mode);
+ }
+ }
+}
+
+void CDisplaySettings::SettingOptionsPreferredStereoscopicViewModesFiller(
+ const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ const CStereoscopicsManager &stereoscopicsManager = CServiceBroker::GetGUI()->GetStereoscopicsManager();
+
+ list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(RENDER_STEREO_MODE_AUTO),
+ RENDER_STEREO_MODE_AUTO); // option for autodetect
+ // don't add "off" to the list of preferred modes as this doesn't make sense
+ for (int i = RENDER_STEREO_MODE_OFF +1; i < RENDER_STEREO_MODE_COUNT; i++)
+ {
+ RENDER_STEREO_MODE mode = (RENDER_STEREO_MODE) i;
+ // also skip "mono" mode which is no real stereoscopic mode
+ if (mode != RENDER_STEREO_MODE_MONO && CServiceBroker::GetRenderSystem()->SupportsStereo(mode))
+ list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(mode), mode);
+ }
+}
+
+void CDisplaySettings::SettingOptionsMonitorsFiller(const SettingConstPtr& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ auto winSystem = CServiceBroker::GetWinSystem();
+ if (!winSystem)
+ return;
+
+ auto settingsComponent = CServiceBroker::GetSettingsComponent();
+ if (!settingsComponent)
+ return;
+
+ auto settings = settingsComponent->GetSettings();
+ if (!settings)
+ return;
+
+ const std::vector<std::string> monitors = winSystem->GetConnectedOutputs();
+ std::string currentMonitor = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+
+ bool foundMonitor = false;
+ for (auto const& monitor : monitors)
+ {
+ if(monitor == currentMonitor)
+ {
+ foundMonitor = true;
+ }
+ list.emplace_back(monitor, monitor);
+ }
+
+ if (!foundMonitor && !current.empty())
+ {
+ // Add current value so no monitor change is triggered when entering the settings screen and
+ // the preferred monitor is preserved
+ list.emplace_back(current, current);
+ }
+}
+
+void CDisplaySettings::ClearCustomResolutions()
+{
+ if (m_resolutions.size() > RES_CUSTOM)
+ {
+ std::vector<RESOLUTION_INFO>::iterator firstCustom = m_resolutions.begin()+RES_CUSTOM;
+ m_resolutions.erase(firstCustom, m_resolutions.end());
+ }
+}
+
+void CDisplaySettings::SettingOptionsCmsModesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(36580), CMS_MODE_3DLUT);
+#ifdef HAVE_LCMS2
+ list.emplace_back(g_localizeStrings.Get(36581), CMS_MODE_PROFILE);
+#endif
+}
+
+void CDisplaySettings::SettingOptionsCmsWhitepointsFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(36586), CMS_WHITEPOINT_D65);
+ list.emplace_back(g_localizeStrings.Get(36587), CMS_WHITEPOINT_D93);
+}
+
+void CDisplaySettings::SettingOptionsCmsPrimariesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(36588), CMS_PRIMARIES_AUTO);
+ list.emplace_back(g_localizeStrings.Get(36589), CMS_PRIMARIES_BT709);
+ list.emplace_back(g_localizeStrings.Get(36579), CMS_PRIMARIES_BT2020);
+ list.emplace_back(g_localizeStrings.Get(36590), CMS_PRIMARIES_170M);
+ list.emplace_back(g_localizeStrings.Get(36591), CMS_PRIMARIES_BT470M);
+ list.emplace_back(g_localizeStrings.Get(36592), CMS_PRIMARIES_BT470BG);
+ list.emplace_back(g_localizeStrings.Get(36593), CMS_PRIMARIES_240M);
+}
+
+void CDisplaySettings::SettingOptionsCmsGammaModesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(36582), CMS_TRC_BT1886);
+ list.emplace_back(g_localizeStrings.Get(36583), CMS_TRC_INPUT_OFFSET);
+ list.emplace_back(g_localizeStrings.Get(36584), CMS_TRC_OUTPUT_OFFSET);
+ list.emplace_back(g_localizeStrings.Get(36585), CMS_TRC_ABSOLUTE);
+}
+