diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/settings/DisplaySettings.cpp | |
parent | Initial commit. (diff) | |
download | kodi-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.cpp | 948 |
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); +} + |