[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / settings / DisplaySettings.cpp
blob031ca0be96b300180d9dd5a31246e60dee692399
1 /*
2 * Copyright (C) 2013-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "DisplaySettings.h"
11 #include "ServiceBroker.h"
12 #include "cores/VideoPlayer/VideoRenderers/ColorManager.h"
13 #include "dialogs/GUIDialogFileBrowser.h"
14 #include "guilib/GUIComponent.h"
15 #include "guilib/LocalizeStrings.h"
16 #include "guilib/StereoscopicsManager.h"
17 #include "messaging/helpers/DialogHelper.h"
18 #include "rendering/RenderSystem.h"
19 #include "settings/AdvancedSettings.h"
20 #include "settings/Settings.h"
21 #include "settings/SettingsComponent.h"
22 #include "settings/lib/Setting.h"
23 #include "settings/lib/SettingDefinitions.h"
24 #include "storage/MediaManager.h"
25 #include "utils/StringUtils.h"
26 #include "utils/Variant.h"
27 #include "utils/XMLUtils.h"
28 #include "utils/log.h"
29 #include "windowing/GraphicContext.h"
30 #include "windowing/WinSystem.h"
32 #include <algorithm>
33 #include <cstdlib>
34 #include <float.h>
35 #include <mutex>
36 #include <string>
37 #include <utility>
38 #include <vector>
40 #ifdef TARGET_WINDOWS
41 #include "rendering/dx/DeviceResources.h"
42 #endif
44 using namespace KODI::MESSAGING;
46 using KODI::MESSAGING::HELPERS::DialogResponse;
48 // 0.1 second increments
49 #define MAX_REFRESH_CHANGE_DELAY 200
51 static RESOLUTION_INFO EmptyResolution;
52 static RESOLUTION_INFO EmptyModifiableResolution;
54 float square_error(float x, float y)
56 float yonx = (x > 0) ? y / x : 0;
57 float xony = (y > 0) ? x / y : 0;
58 return std::max(yonx, xony);
61 static std::string ModeFlagsToString(unsigned int flags, bool identifier)
63 std::string res;
64 if(flags & D3DPRESENTFLAG_INTERLACED)
65 res += "i";
66 else
67 res += "p";
69 if(!identifier)
70 res += " ";
72 if(flags & D3DPRESENTFLAG_MODE3DSBS)
73 res += "sbs";
74 else if(flags & D3DPRESENTFLAG_MODE3DTB)
75 res += "tab";
76 else if(identifier)
77 res += "std";
78 return res;
81 CDisplaySettings::CDisplaySettings()
83 m_resolutions.resize(RES_CUSTOM);
85 m_zoomAmount = 1.0f;
86 m_pixelRatio = 1.0f;
87 m_verticalShift = 0.0f;
88 m_nonLinearStretched = false;
89 m_resolutionChangeAborted = false;
92 CDisplaySettings::~CDisplaySettings() = default;
94 CDisplaySettings& CDisplaySettings::GetInstance()
96 static CDisplaySettings sDisplaySettings;
97 return sDisplaySettings;
100 bool CDisplaySettings::Load(const TiXmlNode *settings)
102 std::unique_lock<CCriticalSection> lock(m_critical);
103 m_calibrations.clear();
105 if (settings == NULL)
106 return false;
108 const TiXmlElement *pElement = settings->FirstChildElement("resolutions");
109 if (!pElement)
111 CLog::Log(LOGERROR, "CDisplaySettings: settings file doesn't contain <resolutions>");
112 return false;
115 const TiXmlElement *pResolution = pElement->FirstChildElement("resolution");
116 while (pResolution)
118 // get the data for this calibration
119 RESOLUTION_INFO cal;
121 XMLUtils::GetString(pResolution, "description", cal.strMode);
122 XMLUtils::GetInt(pResolution, "subtitles", cal.iSubtitles);
123 XMLUtils::GetFloat(pResolution, "pixelratio", cal.fPixelRatio);
124 #ifdef HAVE_X11
125 XMLUtils::GetFloat(pResolution, "refreshrate", cal.fRefreshRate);
126 XMLUtils::GetString(pResolution, "output", cal.strOutput);
127 XMLUtils::GetString(pResolution, "xrandrid", cal.strId);
128 #endif
130 const TiXmlElement *pOverscan = pResolution->FirstChildElement("overscan");
131 if (pOverscan)
133 XMLUtils::GetInt(pOverscan, "left", cal.Overscan.left);
134 XMLUtils::GetInt(pOverscan, "top", cal.Overscan.top);
135 XMLUtils::GetInt(pOverscan, "right", cal.Overscan.right);
136 XMLUtils::GetInt(pOverscan, "bottom", cal.Overscan.bottom);
139 // mark calibration as not updated
140 // we must not delete those, resolution just might not be available
141 cal.iWidth = cal.iHeight = 0;
143 // store calibration, avoid adding duplicates
144 bool found = false;
145 for (ResolutionInfos::const_iterator it = m_calibrations.begin(); it != m_calibrations.end(); ++it)
147 if (StringUtils::EqualsNoCase(it->strMode, cal.strMode))
149 found = true;
150 break;
153 if (!found)
154 m_calibrations.push_back(cal);
156 // iterate around
157 pResolution = pResolution->NextSiblingElement("resolution");
160 ApplyCalibrations();
161 return true;
164 bool CDisplaySettings::Save(TiXmlNode *settings) const
166 if (settings == NULL)
167 return false;
169 std::unique_lock<CCriticalSection> lock(m_critical);
170 TiXmlElement xmlRootElement("resolutions");
171 TiXmlNode *pRoot = settings->InsertEndChild(xmlRootElement);
172 if (pRoot == NULL)
173 return false;
175 // save calibrations
176 for (ResolutionInfos::const_iterator it = m_calibrations.begin(); it != m_calibrations.end(); ++it)
178 // Write the resolution tag
179 TiXmlElement resElement("resolution");
180 TiXmlNode *pNode = pRoot->InsertEndChild(resElement);
181 if (pNode == NULL)
182 return false;
184 // Now write each of the pieces of information we need...
185 XMLUtils::SetString(pNode, "description", it->strMode);
186 XMLUtils::SetInt(pNode, "subtitles", it->iSubtitles);
187 XMLUtils::SetFloat(pNode, "pixelratio", it->fPixelRatio);
188 #ifdef HAVE_X11
189 XMLUtils::SetFloat(pNode, "refreshrate", it->fRefreshRate);
190 XMLUtils::SetString(pNode, "output", it->strOutput);
191 XMLUtils::SetString(pNode, "xrandrid", it->strId);
192 #endif
194 // create the overscan child
195 TiXmlElement overscanElement("overscan");
196 TiXmlNode *pOverscanNode = pNode->InsertEndChild(overscanElement);
197 if (pOverscanNode == NULL)
198 return false;
200 XMLUtils::SetInt(pOverscanNode, "left", it->Overscan.left);
201 XMLUtils::SetInt(pOverscanNode, "top", it->Overscan.top);
202 XMLUtils::SetInt(pOverscanNode, "right", it->Overscan.right);
203 XMLUtils::SetInt(pOverscanNode, "bottom", it->Overscan.bottom);
206 return true;
209 void CDisplaySettings::Clear()
211 std::unique_lock<CCriticalSection> lock(m_critical);
212 m_calibrations.clear();
213 m_resolutions.clear();
214 m_resolutions.resize(RES_CUSTOM);
216 m_zoomAmount = 1.0f;
217 m_pixelRatio = 1.0f;
218 m_verticalShift = 0.0f;
219 m_nonLinearStretched = false;
222 void CDisplaySettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
224 if (setting == NULL)
225 return;
227 const std::string &settingId = setting->GetId();
228 if (settingId == "videoscreen.cms3dlut")
230 std::string path = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
231 VECSOURCES shares;
232 CServiceBroker::GetMediaManager().GetLocalDrives(shares);
233 if (CGUIDialogFileBrowser::ShowAndGetFile(shares, ".3dlut", g_localizeStrings.Get(36580), path))
235 std::static_pointer_cast<CSettingString>(std::const_pointer_cast<CSetting>(setting))->SetValue(path);
238 else if (settingId == "videoscreen.displayprofile")
240 std::string path = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
241 VECSOURCES shares;
242 CServiceBroker::GetMediaManager().GetLocalDrives(shares);
243 if (CGUIDialogFileBrowser::ShowAndGetFile(shares, ".icc|.icm", g_localizeStrings.Get(36581), path))
245 std::static_pointer_cast<CSettingString>(std::const_pointer_cast<CSetting>(setting))->SetValue(path);
250 bool CDisplaySettings::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
252 if (setting == NULL)
253 return false;
255 const std::string &settingId = setting->GetId();
256 if (settingId == CSettings::SETTING_VIDEOSCREEN_RESOLUTION ||
257 settingId == CSettings::SETTING_VIDEOSCREEN_SCREEN)
259 RESOLUTION newRes = RES_DESKTOP;
260 if (settingId == CSettings::SETTING_VIDEOSCREEN_RESOLUTION)
261 newRes = (RESOLUTION)std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
262 else if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREEN)
264 int screen = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
266 // if triggered by a change of screenmode, screen may not have changed
267 if (screen == GetCurrentDisplayMode())
268 return true;
270 // get desktop resolution for screen
271 newRes = GetResolutionForScreen();
274 std::string screenmode = GetStringFromResolution(newRes);
275 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_VIDEOSCREEN_SCREENMODE, screenmode))
276 return false;
279 if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREENMODE)
281 RESOLUTION oldRes = GetCurrentResolution();
282 RESOLUTION newRes = GetResolutionFromString(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
284 SetCurrentResolution(newRes, false);
285 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(newRes, false);
287 // check if the old or the new resolution was/is windowed
288 // in which case we don't show any prompt to the user
289 if (oldRes != RES_WINDOW && newRes != RES_WINDOW && oldRes != newRes)
291 if (!m_resolutionChangeAborted)
293 if (HELPERS::ShowYesNoDialogText(CVariant{13110}, CVariant{13111}, CVariant{""},
294 CVariant{""}, 15000) != DialogResponse::CHOICE_YES)
296 m_resolutionChangeAborted = true;
297 return false;
300 else
301 m_resolutionChangeAborted = false;
304 else if (settingId == CSettings::SETTING_VIDEOSCREEN_MONITOR)
306 auto winSystem = CServiceBroker::GetWinSystem();
307 if (winSystem->SupportsScreenMove())
309 const std::string screen =
310 std::static_pointer_cast<const CSettingString>(setting)->GetValue();
311 const unsigned int screenIdx = winSystem->GetScreenId(screen);
312 winSystem->MoveToScreen(screenIdx);
314 winSystem->UpdateResolutions();
315 RESOLUTION newRes = GetResolutionForScreen();
317 SetCurrentResolution(newRes, false);
318 winSystem->GetGfxContext().SetVideoResolution(newRes, true);
320 if (!m_resolutionChangeAborted)
322 if (HELPERS::ShowYesNoDialogText(CVariant{13110}, CVariant{13111}, CVariant{""}, CVariant{""},
323 10000) != DialogResponse::CHOICE_YES)
325 m_resolutionChangeAborted = true;
326 return false;
329 else
330 m_resolutionChangeAborted = false;
332 return true;
334 else if (settingId == CSettings::SETTING_VIDEOSCREEN_10BITSURFACES)
336 #ifdef TARGET_WINDOWS
337 DX::DeviceResources::Get()->ApplyDisplaySettings();
338 return true;
339 #endif
341 #if defined(HAVE_X11) || defined(TARGET_WINDOWS_DESKTOP) || defined(TARGET_DARWIN_OSX)
342 else if (settingId == CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS)
344 auto winSystem = CServiceBroker::GetWinSystem();
345 #if defined(HAVE_X11)
346 winSystem->UpdateResolutions();
347 #elif defined(TARGET_WINDOWS_DESKTOP) || defined(TARGET_DARWIN_OSX)
348 CGraphicContext& gfxContext = winSystem->GetGfxContext();
349 gfxContext.SetVideoResolution(gfxContext.GetVideoResolution(), true);
350 #endif
352 #endif
354 return true;
357 bool CDisplaySettings::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
358 const char* oldSettingId,
359 const TiXmlNode* oldSettingNode)
361 if (setting == NULL)
362 return false;
364 const std::string &settingId = setting->GetId();
365 if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREENMODE)
367 std::shared_ptr<CSettingString> screenmodeSetting = std::static_pointer_cast<CSettingString>(setting);
368 std::string screenmode = screenmodeSetting->GetValue();
369 // in Eden there was no character ("i" or "p") indicating interlaced/progressive
370 // at the end so we just add a "p" and assume progressive
371 // no 3d mode existed before, so just assume std modes
372 if (screenmode.size() == 20)
373 return screenmodeSetting->SetValue(screenmode + "pstd");
374 if (screenmode.size() == 21)
375 return screenmodeSetting->SetValue(screenmode + "std");
377 else if (settingId == CSettings::SETTING_VIDEOSCREEN_PREFEREDSTEREOSCOPICMODE)
379 std::shared_ptr<CSettingInt> stereomodeSetting = std::static_pointer_cast<CSettingInt>(setting);
380 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
381 STEREOSCOPIC_PLAYBACK_MODE playbackMode = (STEREOSCOPIC_PLAYBACK_MODE) settings->GetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE);
382 if (stereomodeSetting->GetValue() == RENDER_STEREO_MODE_OFF)
384 // if preferred playback mode was OFF, update playback mode to ignore
385 if (playbackMode == STEREOSCOPIC_PLAYBACK_MODE_PREFERRED)
386 settings->SetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE, STEREOSCOPIC_PLAYBACK_MODE_IGNORE);
387 return stereomodeSetting->SetValue(RENDER_STEREO_MODE_AUTO);
389 else if (stereomodeSetting->GetValue() == RENDER_STEREO_MODE_MONO)
391 // if preferred playback mode was MONO, update playback mode
392 if (playbackMode == STEREOSCOPIC_PLAYBACK_MODE_PREFERRED)
393 settings->SetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE, STEREOSCOPIC_PLAYBACK_MODE_MONO);
394 return stereomodeSetting->SetValue(RENDER_STEREO_MODE_AUTO);
398 return false;
401 void CDisplaySettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
403 if (!setting)
404 return;
406 const std::string& settingId = setting->GetId();
407 if (settingId == CSettings::SETTING_VIDEOSCREEN_WHITELIST)
408 CResolutionUtils::PrintWhitelist();
411 void CDisplaySettings::SetMonitor(const std::string& monitor)
413 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
414 const std::string curMonitor = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
415 if (curMonitor != monitor)
417 m_resolutionChangeAborted = true;
418 settings->SetString(CSettings::SETTING_VIDEOSCREEN_MONITOR, monitor);
422 void CDisplaySettings::SetCurrentResolution(RESOLUTION resolution, bool save /* = false */)
424 if (resolution == RES_WINDOW && !CServiceBroker::GetWinSystem()->CanDoWindowed())
425 resolution = RES_DESKTOP;
427 if (save)
429 // Save videoscreen.screenmode setting
430 std::string mode = GetStringFromResolution(resolution);
431 CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(
432 CSettings::SETTING_VIDEOSCREEN_SCREENMODE, mode);
434 // Check if videoscreen.screen setting also needs to be saved
435 // e.g. if ToggleFullscreen is called
436 int currentDisplayMode = GetCurrentDisplayMode();
437 int currentDisplayModeSetting = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN);
438 if (currentDisplayMode != currentDisplayModeSetting)
440 CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN, currentDisplayMode);
443 else if (resolution != m_currentResolution)
445 m_currentResolution = resolution;
446 SetChanged();
450 RESOLUTION CDisplaySettings::GetDisplayResolution() const
452 return GetResolutionFromString(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_SCREENMODE));
455 const RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(size_t index) const
457 std::unique_lock<CCriticalSection> lock(m_critical);
458 if (index >= m_resolutions.size())
459 return EmptyResolution;
461 return m_resolutions[index];
464 const RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(RESOLUTION resolution) const
466 if (resolution <= RES_INVALID)
467 return EmptyResolution;
469 return GetResolutionInfo((size_t)resolution);
472 RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(size_t index)
474 std::unique_lock<CCriticalSection> lock(m_critical);
475 if (index >= m_resolutions.size())
477 EmptyModifiableResolution = RESOLUTION_INFO();
478 return EmptyModifiableResolution;
481 return m_resolutions[index];
484 RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(RESOLUTION resolution)
486 if (resolution <= RES_INVALID)
488 EmptyModifiableResolution = RESOLUTION_INFO();
489 return EmptyModifiableResolution;
492 return GetResolutionInfo((size_t)resolution);
495 void CDisplaySettings::AddResolutionInfo(const RESOLUTION_INFO &resolution)
497 std::unique_lock<CCriticalSection> lock(m_critical);
498 RESOLUTION_INFO res(resolution);
500 if((res.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
502 /* add corrections for some special case modes frame packing modes */
504 if(res.iScreenWidth == 1920
505 && res.iScreenHeight == 2205)
507 res.iBlanking = 45;
508 res.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
511 if(res.iScreenWidth == 1280
512 && res.iScreenHeight == 1470)
514 res.iBlanking = 30;
515 res.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
518 m_resolutions.push_back(res);
521 void CDisplaySettings::ApplyCalibrations()
523 std::unique_lock<CCriticalSection> lock(m_critical);
524 // apply all calibrations to the resolutions
525 for (ResolutionInfos::const_iterator itCal = m_calibrations.begin(); itCal != m_calibrations.end(); ++itCal)
527 // find resolutions
528 for (size_t res = RES_DESKTOP; res < m_resolutions.size(); ++res)
530 if (StringUtils::EqualsNoCase(itCal->strMode, m_resolutions[res].strMode))
532 // overscan
533 m_resolutions[res].Overscan.left = itCal->Overscan.left;
534 if (m_resolutions[res].Overscan.left < -m_resolutions[res].iWidth/4)
535 m_resolutions[res].Overscan.left = -m_resolutions[res].iWidth/4;
536 if (m_resolutions[res].Overscan.left > m_resolutions[res].iWidth/4)
537 m_resolutions[res].Overscan.left = m_resolutions[res].iWidth/4;
539 m_resolutions[res].Overscan.top = itCal->Overscan.top;
540 if (m_resolutions[res].Overscan.top < -m_resolutions[res].iHeight/4)
541 m_resolutions[res].Overscan.top = -m_resolutions[res].iHeight/4;
542 if (m_resolutions[res].Overscan.top > m_resolutions[res].iHeight/4)
543 m_resolutions[res].Overscan.top = m_resolutions[res].iHeight/4;
545 m_resolutions[res].Overscan.right = itCal->Overscan.right;
546 if (m_resolutions[res].Overscan.right < m_resolutions[res].iWidth / 2)
547 m_resolutions[res].Overscan.right = m_resolutions[res].iWidth / 2;
548 if (m_resolutions[res].Overscan.right > m_resolutions[res].iWidth * 3/2)
549 m_resolutions[res].Overscan.right = m_resolutions[res].iWidth *3/2;
551 m_resolutions[res].Overscan.bottom = itCal->Overscan.bottom;
552 if (m_resolutions[res].Overscan.bottom < m_resolutions[res].iHeight / 2)
553 m_resolutions[res].Overscan.bottom = m_resolutions[res].iHeight / 2;
554 if (m_resolutions[res].Overscan.bottom > m_resolutions[res].iHeight * 3/2)
555 m_resolutions[res].Overscan.bottom = m_resolutions[res].iHeight * 3/2;
557 m_resolutions[res].iSubtitles = itCal->iSubtitles;
558 if (m_resolutions[res].iSubtitles < 0)
559 m_resolutions[res].iSubtitles = 0;
560 if (m_resolutions[res].iSubtitles > m_resolutions[res].iHeight * 3 / 2)
561 m_resolutions[res].iSubtitles = m_resolutions[res].iHeight * 3 / 2;
563 m_resolutions[res].fPixelRatio = itCal->fPixelRatio;
564 if (m_resolutions[res].fPixelRatio < 0.5f)
565 m_resolutions[res].fPixelRatio = 0.5f;
566 if (m_resolutions[res].fPixelRatio > 2.0f)
567 m_resolutions[res].fPixelRatio = 2.0f;
568 break;
574 void CDisplaySettings::UpdateCalibrations()
576 std::unique_lock<CCriticalSection> lock(m_critical);
578 if (m_resolutions.size() <= RES_DESKTOP)
579 return;
581 // Add new (unique) resolutions
582 for (ResolutionInfos::const_iterator res(m_resolutions.cbegin() + RES_CUSTOM); res != m_resolutions.cend(); ++res)
583 if (std::find_if(m_calibrations.cbegin(), m_calibrations.cend(),
584 [&](const RESOLUTION_INFO& info) { return StringUtils::EqualsNoCase(res->strMode, info.strMode); }) == m_calibrations.cend())
585 m_calibrations.push_back(*res);
587 for (auto &cal : m_calibrations)
589 ResolutionInfos::const_iterator res(std::find_if(m_resolutions.cbegin() + RES_DESKTOP, m_resolutions.cend(),
590 [&](const RESOLUTION_INFO& info) { return StringUtils::EqualsNoCase(cal.strMode, info.strMode); }));
592 if (res != m_resolutions.cend())
594 //! @todo erase calibrations with default values
595 cal = *res;
600 void CDisplaySettings::ClearCalibrations()
602 std::unique_lock<CCriticalSection> lock(m_critical);
603 m_calibrations.clear();
606 DisplayMode CDisplaySettings::GetCurrentDisplayMode() const
608 if (GetCurrentResolution() == RES_WINDOW)
609 return DM_WINDOWED;
611 return DM_FULLSCREEN;
614 RESOLUTION CDisplaySettings::FindBestMatchingResolution(const std::map<RESOLUTION, RESOLUTION_INFO> &resolutionInfos, int width, int height, float refreshrate, unsigned flags)
616 // find the closest match to these in our res vector. If we have the screen, we score the res
617 RESOLUTION bestRes = RES_DESKTOP;
618 float bestScore = FLT_MAX;
619 flags &= D3DPRESENTFLAG_MODEMASK;
621 for (std::map<RESOLUTION, RESOLUTION_INFO>::const_iterator it = resolutionInfos.begin(); it != resolutionInfos.end(); ++it)
623 const RESOLUTION_INFO &info = it->second;
625 if ((info.dwFlags & D3DPRESENTFLAG_MODEMASK) != flags)
626 continue;
628 float score = 10 * (square_error((float)info.iScreenWidth, (float)width) +
629 square_error((float)info.iScreenHeight, (float)height) +
630 square_error(info.fRefreshRate, refreshrate));
631 if (score < bestScore)
633 bestScore = score;
634 bestRes = it->first;
638 return bestRes;
641 RESOLUTION CDisplaySettings::GetResolutionFromString(const std::string &strResolution)
644 if (strResolution == "DESKTOP")
645 return RES_DESKTOP;
646 else if (strResolution == "WINDOW")
647 return RES_WINDOW;
648 else if (strResolution.size() >= 20)
650 // format: WWWWWHHHHHRRR.RRRRRP333, where W = width, H = height, R = refresh, P = interlace, 3 = stereo mode
651 int width = std::strtol(StringUtils::Mid(strResolution, 0,5).c_str(), NULL, 10);
652 int height = std::strtol(StringUtils::Mid(strResolution, 5,5).c_str(), NULL, 10);
653 float refresh = (float)std::strtod(StringUtils::Mid(strResolution, 10,9).c_str(), NULL);
654 unsigned flags = 0;
656 // look for 'i' and treat everything else as progressive,
657 if(StringUtils::Mid(strResolution, 19,1) == "i")
658 flags |= D3DPRESENTFLAG_INTERLACED;
660 if(StringUtils::Mid(strResolution, 20,3) == "sbs")
661 flags |= D3DPRESENTFLAG_MODE3DSBS;
662 else if(StringUtils::Mid(strResolution, 20,3) == "tab")
663 flags |= D3DPRESENTFLAG_MODE3DTB;
665 std::map<RESOLUTION, RESOLUTION_INFO> resolutionInfos;
666 for (size_t resolution = RES_DESKTOP; resolution < CDisplaySettings::GetInstance().ResolutionInfoSize(); resolution++)
667 resolutionInfos.insert(std::make_pair((RESOLUTION)resolution, CDisplaySettings::GetInstance().GetResolutionInfo(resolution)));
669 return FindBestMatchingResolution(resolutionInfos, width, height, refresh, flags);
672 return RES_DESKTOP;
675 std::string CDisplaySettings::GetStringFromResolution(RESOLUTION resolution, float refreshrate /* = 0.0f */)
677 if (resolution == RES_WINDOW)
678 return "WINDOW";
680 if (resolution >= RES_DESKTOP && resolution < (RESOLUTION)CDisplaySettings::GetInstance().ResolutionInfoSize())
682 const RESOLUTION_INFO &info = CDisplaySettings::GetInstance().GetResolutionInfo(resolution);
683 // also handle RES_DESKTOP resolutions with non-default refresh rates
684 if (resolution != RES_DESKTOP || (refreshrate > 0.0f && refreshrate != info.fRefreshRate))
686 return StringUtils::Format("{:05}{:05}{:09.5f}{}", info.iScreenWidth, info.iScreenHeight,
687 refreshrate > 0.0f ? refreshrate : info.fRefreshRate,
688 ModeFlagsToString(info.dwFlags, true));
692 return "DESKTOP";
695 RESOLUTION CDisplaySettings::GetResolutionForScreen()
697 DisplayMode mode = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN);
698 if (mode == DM_WINDOWED)
699 return RES_WINDOW;
701 return RES_DESKTOP;
704 static inline bool ModeSort(const StringSettingOption& i, const StringSettingOption& j)
706 return (i.value > j.value);
709 void CDisplaySettings::SettingOptionsModesFiller(const std::shared_ptr<const CSetting>& setting,
710 std::vector<StringSettingOption>& list,
711 std::string& current,
712 void* data)
714 for (auto index = (unsigned int)RES_CUSTOM; index < CDisplaySettings::GetInstance().ResolutionInfoSize(); ++index)
716 const auto mode = CDisplaySettings::GetInstance().GetResolutionInfo(index);
718 if (mode.dwFlags ^ D3DPRESENTFLAG_INTERLACED)
720 auto setting = GetStringFromResolution((RESOLUTION)index, mode.fRefreshRate);
722 list.emplace_back(
723 StringUtils::Format("{}x{}{} {:0.2f}Hz", mode.iScreenWidth, mode.iScreenHeight,
724 ModeFlagsToString(mode.dwFlags, false), mode.fRefreshRate),
725 setting);
729 std::sort(list.begin(), list.end(), ModeSort);
732 void CDisplaySettings::SettingOptionsRefreshChangeDelaysFiller(
733 const SettingConstPtr& setting,
734 std::vector<IntegerSettingOption>& list,
735 int& current,
736 void* data)
738 list.emplace_back(g_localizeStrings.Get(13551), 0);
740 for (int i = 1; i <= MAX_REFRESH_CHANGE_DELAY; i++)
741 list.emplace_back(
742 StringUtils::Format(g_localizeStrings.Get(13553), static_cast<double>(i) / 10.0), i);
745 void CDisplaySettings::SettingOptionsRefreshRatesFiller(const SettingConstPtr& setting,
746 std::vector<StringSettingOption>& list,
747 std::string& current,
748 void* data)
750 // get the proper resolution
751 RESOLUTION res = CDisplaySettings::GetInstance().GetDisplayResolution();
752 if (res < RES_WINDOW)
753 return;
755 // only add "Windowed" if in windowed mode
756 if (res == RES_WINDOW)
758 current = "WINDOW";
759 list.emplace_back(g_localizeStrings.Get(242), current);
760 return;
763 RESOLUTION_INFO resInfo = CDisplaySettings::GetInstance().GetResolutionInfo(res);
764 // The only meaningful parts of res here are iScreenWidth, iScreenHeight
765 std::vector<REFRESHRATE> refreshrates = CServiceBroker::GetWinSystem()->RefreshRates(resInfo.iScreenWidth, resInfo.iScreenHeight, resInfo.dwFlags);
767 bool match = false;
768 for (std::vector<REFRESHRATE>::const_iterator refreshrate = refreshrates.begin(); refreshrate != refreshrates.end(); ++refreshrate)
770 std::string screenmode = GetStringFromResolution((RESOLUTION)refreshrate->ResInfo_Index, refreshrate->RefreshRate);
771 if (!match && StringUtils::EqualsNoCase(std::static_pointer_cast<const CSettingString>(setting)->GetValue(), screenmode))
772 match = true;
773 list.emplace_back(StringUtils::Format("{:.2f}", refreshrate->RefreshRate), screenmode);
776 if (!match)
777 current = GetStringFromResolution(res, CServiceBroker::GetWinSystem()->DefaultRefreshRate(refreshrates).RefreshRate);
780 void CDisplaySettings::SettingOptionsResolutionsFiller(const SettingConstPtr& setting,
781 std::vector<IntegerSettingOption>& list,
782 int& current,
783 void* data)
785 RESOLUTION res = CDisplaySettings::GetInstance().GetDisplayResolution();
786 RESOLUTION_INFO info = CDisplaySettings::GetInstance().GetResolutionInfo(res);
787 if (res == RES_WINDOW)
789 current = res;
790 list.emplace_back(g_localizeStrings.Get(242), res);
792 else
794 std::map<RESOLUTION, RESOLUTION_INFO> resolutionInfos;
795 std::vector<RESOLUTION_WHR> resolutions = CServiceBroker::GetWinSystem()->ScreenResolutions(info.fRefreshRate);
796 for (std::vector<RESOLUTION_WHR>::const_iterator resolution = resolutions.begin(); resolution != resolutions.end(); ++resolution)
798 const std::string resLabel =
799 StringUtils::Format("{}x{}{}{}", resolution->m_screenWidth, resolution->m_screenHeight,
800 ModeFlagsToString(resolution->flags, false),
801 resolution->width > resolution->m_screenWidth &&
802 resolution->height > resolution->m_screenHeight
803 ? " (HiDPI)"
804 : "");
805 list.emplace_back(resLabel, resolution->ResInfo_Index);
807 resolutionInfos.insert(std::make_pair((RESOLUTION)resolution->ResInfo_Index, CDisplaySettings::GetInstance().GetResolutionInfo(resolution->ResInfo_Index)));
810 // ids are unique, so try to find a match by id first. Then resort to best matching resolution.
811 if (!info.strId.empty())
813 const auto it = std::find_if(resolutionInfos.begin(), resolutionInfos.end(),
814 [&](const std::pair<RESOLUTION, RESOLUTION_INFO>& resItem) {
815 return info.strId == resItem.second.strId;
817 current =
818 it != resolutionInfos.end()
819 ? it->first
820 : FindBestMatchingResolution(resolutionInfos, info.iScreenWidth, info.iScreenHeight,
821 info.fRefreshRate, info.dwFlags);
823 else
825 current = FindBestMatchingResolution(resolutionInfos, info.iScreenWidth, info.iScreenHeight,
826 info.fRefreshRate, info.dwFlags);
831 void CDisplaySettings::SettingOptionsDispModeFiller(const SettingConstPtr& setting,
832 std::vector<IntegerSettingOption>& list,
833 int& current,
834 void* data)
836 // The user should only be able to disable windowed modes with the canwindowed
837 // setting. When the user sets canwindowed to true but the windowing system
838 // does not support windowed modes, we would just shoot ourselves in the foot
839 // by offering the option.
840 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_canWindowed && CServiceBroker::GetWinSystem()->CanDoWindowed())
841 list.emplace_back(g_localizeStrings.Get(242), DM_WINDOWED);
843 list.emplace_back(g_localizeStrings.Get(244), DM_FULLSCREEN);
846 void CDisplaySettings::SettingOptionsStereoscopicModesFiller(
847 const SettingConstPtr& setting,
848 std::vector<IntegerSettingOption>& list,
849 int& current,
850 void* data)
852 CGUIComponent *gui = CServiceBroker::GetGUI();
853 if (gui != nullptr)
855 const CStereoscopicsManager &stereoscopicsManager = gui->GetStereoscopicsManager();
857 for (int i = RENDER_STEREO_MODE_OFF; i < RENDER_STEREO_MODE_COUNT; i++)
859 RENDER_STEREO_MODE mode = (RENDER_STEREO_MODE) i;
860 if (CServiceBroker::GetRenderSystem()->SupportsStereo(mode))
861 list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(mode), mode);
866 void CDisplaySettings::SettingOptionsPreferredStereoscopicViewModesFiller(
867 const SettingConstPtr& setting,
868 std::vector<IntegerSettingOption>& list,
869 int& current,
870 void* data)
872 const CStereoscopicsManager &stereoscopicsManager = CServiceBroker::GetGUI()->GetStereoscopicsManager();
874 list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(RENDER_STEREO_MODE_AUTO),
875 RENDER_STEREO_MODE_AUTO); // option for autodetect
876 // don't add "off" to the list of preferred modes as this doesn't make sense
877 for (int i = RENDER_STEREO_MODE_OFF +1; i < RENDER_STEREO_MODE_COUNT; i++)
879 RENDER_STEREO_MODE mode = (RENDER_STEREO_MODE) i;
880 // also skip "mono" mode which is no real stereoscopic mode
881 if (mode != RENDER_STEREO_MODE_MONO && CServiceBroker::GetRenderSystem()->SupportsStereo(mode))
882 list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(mode), mode);
886 void CDisplaySettings::SettingOptionsMonitorsFiller(const SettingConstPtr& setting,
887 std::vector<StringSettingOption>& list,
888 std::string& current,
889 void* data)
891 auto winSystem = CServiceBroker::GetWinSystem();
892 if (!winSystem)
893 return;
895 auto settingsComponent = CServiceBroker::GetSettingsComponent();
896 if (!settingsComponent)
897 return;
899 auto settings = settingsComponent->GetSettings();
900 if (!settings)
901 return;
903 const std::vector<std::string> monitors = winSystem->GetConnectedOutputs();
904 std::string currentMonitor = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
906 bool foundMonitor = false;
907 for (auto const& monitor : monitors)
909 if(monitor == currentMonitor)
911 foundMonitor = true;
913 list.emplace_back(monitor, monitor);
916 if (!foundMonitor && !current.empty())
918 // Add current value so no monitor change is triggered when entering the settings screen and
919 // the preferred monitor is preserved
920 list.emplace_back(current, current);
924 void CDisplaySettings::ClearCustomResolutions()
926 if (m_resolutions.size() > RES_CUSTOM)
928 std::vector<RESOLUTION_INFO>::iterator firstCustom = m_resolutions.begin()+RES_CUSTOM;
929 m_resolutions.erase(firstCustom, m_resolutions.end());
933 void CDisplaySettings::SettingOptionsCmsModesFiller(const SettingConstPtr& setting,
934 std::vector<IntegerSettingOption>& list,
935 int& current,
936 void* data)
938 list.emplace_back(g_localizeStrings.Get(36580), CMS_MODE_3DLUT);
939 #ifdef HAVE_LCMS2
940 list.emplace_back(g_localizeStrings.Get(36581), CMS_MODE_PROFILE);
941 #endif
944 void CDisplaySettings::SettingOptionsCmsWhitepointsFiller(const SettingConstPtr& setting,
945 std::vector<IntegerSettingOption>& list,
946 int& current,
947 void* data)
949 list.emplace_back(g_localizeStrings.Get(36586), CMS_WHITEPOINT_D65);
950 list.emplace_back(g_localizeStrings.Get(36587), CMS_WHITEPOINT_D93);
953 void CDisplaySettings::SettingOptionsCmsPrimariesFiller(const SettingConstPtr& setting,
954 std::vector<IntegerSettingOption>& list,
955 int& current,
956 void* data)
958 list.emplace_back(g_localizeStrings.Get(36588), CMS_PRIMARIES_AUTO);
959 list.emplace_back(g_localizeStrings.Get(36589), CMS_PRIMARIES_BT709);
960 list.emplace_back(g_localizeStrings.Get(36579), CMS_PRIMARIES_BT2020);
961 list.emplace_back(g_localizeStrings.Get(36590), CMS_PRIMARIES_170M);
962 list.emplace_back(g_localizeStrings.Get(36591), CMS_PRIMARIES_BT470M);
963 list.emplace_back(g_localizeStrings.Get(36592), CMS_PRIMARIES_BT470BG);
964 list.emplace_back(g_localizeStrings.Get(36593), CMS_PRIMARIES_240M);
967 void CDisplaySettings::SettingOptionsCmsGammaModesFiller(const SettingConstPtr& setting,
968 std::vector<IntegerSettingOption>& list,
969 int& current,
970 void* data)
972 list.emplace_back(g_localizeStrings.Get(36582), CMS_TRC_BT1886);
973 list.emplace_back(g_localizeStrings.Get(36583), CMS_TRC_INPUT_OFFSET);
974 list.emplace_back(g_localizeStrings.Get(36584), CMS_TRC_OUTPUT_OFFSET);
975 list.emplace_back(g_localizeStrings.Get(36585), CMS_TRC_ABSOLUTE);