[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / platform / win32 / WIN32Util.cpp
blob29658a42a9b3ef9a2ae95a9271b5cc09f56e2ad6
1 /*
2 * Copyright (C) 2005-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 "WIN32Util.h"
11 #include "CompileInfo.h"
12 #include "ServiceBroker.h"
13 #include "Util.h"
14 #include "WindowHelper.h"
15 #include "guilib/LocalizeStrings.h"
16 #include "my_ntddscsi.h"
17 #include "rendering/dx/DirectXHelper.h"
18 #include "storage/MediaManager.h"
19 #include "storage/cdioSupport.h"
20 #include "utils/CharsetConverter.h"
21 #include "utils/StringUtils.h"
22 #include "utils/SystemInfo.h"
23 #include "utils/URIUtils.h"
24 #include "utils/log.h"
26 #include "platform/win32/CharsetConverter.h"
28 #include <PowrProf.h>
30 #ifdef TARGET_WINDOWS_DESKTOP
31 #include <cassert>
32 #endif
33 #include <array>
34 #include <locale.h>
35 #include <sstream>
37 #include <shellapi.h>
38 #include <shlobj.h>
39 #include <winioctl.h>
41 #ifdef TARGET_WINDOWS_DESKTOP
42 extern HWND g_hWnd;
43 #endif
45 using namespace MEDIA_DETECT;
47 #ifdef TARGET_WINDOWS_STORE
48 #include "platform/win10/AsyncHelpers.h"
50 #include <ppltasks.h>
51 #include <winrt/Windows.Devices.Display.Core.h>
52 #include <winrt/Windows.Devices.Power.h>
53 #include <winrt/Windows.Foundation.Collections.h>
54 #include <winrt/Windows.Graphics.Display.Core.h>
55 #include <winrt/Windows.Storage.h>
57 using namespace winrt::Windows::Devices::Power;
58 using namespace winrt::Windows::Devices::Display::Core;
59 using namespace winrt::Windows::Graphics::Display;
60 using namespace winrt::Windows::Graphics::Display::Core;
61 using namespace winrt::Windows::Storage;
62 #endif
64 void VideoDriverInfo::Log()
66 if (!valid)
68 CLog::LogF(LOGERROR, "video driver version information is not valid");
69 return;
72 if (vendorId == PCIV_NVIDIA)
73 CLog::LogF(LOGINFO, "video driver version is {} {}.{} ({})", DX::GetGFXProviderName(vendorId),
74 majorVersion, minorVersion, version);
75 else
76 CLog::LogF(LOGINFO, "video driver version is {} {}", DX::GetGFXProviderName(vendorId), version);
79 CWIN32Util::CWIN32Util(void)
83 CWIN32Util::~CWIN32Util(void)
87 int CWIN32Util::GetDriveStatus(const std::string &strPath, bool bStatusEx)
89 #ifdef TARGET_WINDOWS_STORE
90 CLog::LogF(LOGDEBUG, "is not implemented");
91 CLog::LogF(LOGDEBUG, "Could not determine tray status {}", GetLastError());
92 return -1;
93 #else
94 using KODI::PLATFORM::WINDOWS::ToW;
96 auto strPathW = ToW(strPath);
97 HANDLE hDevice; // handle to the drive to be examined
98 int iResult; // results flag
99 ULONG ulChanges=0;
100 DWORD dwBytesReturned;
101 T_SPDT_SBUF sptd_sb = {}; // SCSI Pass Through Direct variable.
102 byte DataBuf[8] = {}; // Buffer for holding data to/from drive.
104 CLog::LogF(LOGDEBUG, "Requesting status for drive {}.", strPath);
106 hDevice = CreateFile( strPathW.c_str(), // drive
107 0, // no access to the drive
108 FILE_SHARE_READ, // share mode
109 NULL, // default security attributes
110 OPEN_EXISTING, // disposition
111 FILE_ATTRIBUTE_READONLY, // file attributes
112 NULL);
114 if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
116 CLog::LogF(LOGERROR, "Failed to CreateFile for {}.", strPath);
117 return -1;
120 CLog::LogF(LOGDEBUG, "Requesting media status for drive {}.", strPath);
121 iResult = DeviceIoControl((HANDLE) hDevice, // handle to device
122 IOCTL_STORAGE_CHECK_VERIFY2, // dwIoControlCode
123 NULL, // lpInBuffer
124 0, // nInBufferSize
125 &ulChanges, // lpOutBuffer
126 sizeof(ULONG), // nOutBufferSize
127 &dwBytesReturned , // number of bytes returned
128 NULL ); // OVERLAPPED structure
130 CloseHandle(hDevice);
132 if(iResult == 1)
133 return 2;
135 // don't request the tray status as we often doesn't need it
136 if(!bStatusEx)
137 return 0;
139 hDevice = CreateFile( strPathW.c_str(),
140 GENERIC_READ | GENERIC_WRITE,
141 FILE_SHARE_READ | FILE_SHARE_WRITE,
142 NULL,
143 OPEN_EXISTING,
144 FILE_ATTRIBUTE_READONLY,
145 NULL);
147 if (hDevice == INVALID_HANDLE_VALUE)
149 CLog::LogF(LOGERROR, "Failed to CreateFile2 for {}.", strPath);
150 return -1;
153 sptd_sb.sptd.Length=sizeof(SCSI_PASS_THROUGH_DIRECT);
154 sptd_sb.sptd.PathId=0;
155 sptd_sb.sptd.TargetId=0;
156 sptd_sb.sptd.Lun=0;
157 sptd_sb.sptd.CdbLength=10;
158 sptd_sb.sptd.SenseInfoLength=MAX_SENSE_LEN;
159 sptd_sb.sptd.DataIn=SCSI_IOCTL_DATA_IN;
160 sptd_sb.sptd.DataTransferLength=sizeof(DataBuf);
161 sptd_sb.sptd.TimeOutValue=2;
162 sptd_sb.sptd.DataBuffer=(PVOID)&(DataBuf);
163 sptd_sb.sptd.SenseInfoOffset=sizeof(SCSI_PASS_THROUGH_DIRECT);
165 sptd_sb.sptd.Cdb[0]=0x4a;
166 sptd_sb.sptd.Cdb[1]=1;
167 sptd_sb.sptd.Cdb[2]=0;
168 sptd_sb.sptd.Cdb[3]=0;
169 sptd_sb.sptd.Cdb[4]=0x10;
170 sptd_sb.sptd.Cdb[5]=0;
171 sptd_sb.sptd.Cdb[6]=0;
172 sptd_sb.sptd.Cdb[7]=0;
173 sptd_sb.sptd.Cdb[8]=8;
174 sptd_sb.sptd.Cdb[9]=0;
175 sptd_sb.sptd.Cdb[10]=0;
176 sptd_sb.sptd.Cdb[11]=0;
177 sptd_sb.sptd.Cdb[12]=0;
178 sptd_sb.sptd.Cdb[13]=0;
179 sptd_sb.sptd.Cdb[14]=0;
180 sptd_sb.sptd.Cdb[15]=0;
182 //Send the command to drive
183 CLog::LogF(LOGDEBUG, "Requesting tray status for drive {}.", strPath);
184 iResult = DeviceIoControl((HANDLE) hDevice,
185 IOCTL_SCSI_PASS_THROUGH_DIRECT,
186 (PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
187 (PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
188 &dwBytesReturned,
189 NULL);
191 CloseHandle(hDevice);
193 if(iResult)
196 if(DataBuf[5] == 0) // tray close
197 return 0;
198 else if(DataBuf[5] == 1) // tray open
199 return 1;
200 else
201 return 2; // tray closed, media present
203 CLog::LogF(LOGERROR, "Could not determine tray status {}", GetLastError());
204 return -1;
205 #endif
208 char CWIN32Util::FirstDriveFromMask (ULONG unitmask)
210 char i;
211 for (i = 0; i < 26; ++i)
213 if (unitmask & 0x1) break;
214 unitmask = unitmask >> 1;
216 return (i + 'A');
219 bool CWIN32Util::XBMCShellExecute(const std::string &strPath, bool bWaitForScriptExit)
221 #ifdef TARGET_WINDOWS_STORE
222 CLog::LogF(LOGDEBUG, "s not implemented");
223 return false;
224 #else
225 std::string strCommand = strPath;
226 std::string strExe = strPath;
227 std::string strParams;
228 std::string strWorkingDir;
230 StringUtils::Trim(strCommand);
231 if (strCommand.empty())
233 return false;
235 size_t iIndex = std::string::npos;
236 char split = ' ';
237 if (strCommand[0] == '\"')
239 split = '\"';
241 iIndex = strCommand.find(split, 1);
242 if (iIndex != std::string::npos)
244 strExe = strCommand.substr(0, iIndex + 1);
245 strParams = strCommand.substr(iIndex + 1);
248 StringUtils::Replace(strExe, "\"", "");
250 strWorkingDir = strExe;
251 iIndex = strWorkingDir.rfind('\\');
252 if(iIndex != std::string::npos)
254 strWorkingDir[iIndex+1] = '\0';
257 std::wstring WstrExe, WstrParams, WstrWorkingDir;
258 g_charsetConverter.utf8ToW(strExe, WstrExe);
259 g_charsetConverter.utf8ToW(strParams, WstrParams);
260 g_charsetConverter.utf8ToW(strWorkingDir, WstrWorkingDir);
262 bool ret;
263 SHELLEXECUTEINFOW ShExecInfo = {};
264 ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
265 ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
266 ShExecInfo.hwnd = NULL;
267 ShExecInfo.lpVerb = NULL;
268 ShExecInfo.lpFile = WstrExe.c_str();
269 ShExecInfo.lpParameters = WstrParams.c_str();
270 ShExecInfo.lpDirectory = WstrWorkingDir.c_str();
271 ShExecInfo.nShow = SW_SHOW;
272 ShExecInfo.hInstApp = NULL;
274 g_windowHelper.StopThread();
276 LockSetForegroundWindow(LSFW_UNLOCK);
277 ShowWindow(g_hWnd,SW_MINIMIZE);
278 ret = ShellExecuteExW(&ShExecInfo) == TRUE;
279 g_windowHelper.SetHANDLE(ShExecInfo.hProcess);
281 // ShellExecute doesn't return the window of the started process
282 // we need to gather it from somewhere to allow switch back to XBMC
283 // when a program is minimized instead of stopped.
284 //g_windowHelper.SetHWND(ShExecInfo.hwnd);
285 g_windowHelper.Create();
287 if(bWaitForScriptExit)
289 //! @todo Pause music and video playback
290 WaitForSingleObject(ShExecInfo.hProcess,INFINITE);
293 return ret;
294 #endif
297 std::string CWIN32Util::GetResInfoString()
299 #ifdef TARGET_WINDOWS_STORE
300 auto hdmiInfo = HdmiDisplayInformation::GetForCurrentView();
301 if (hdmiInfo) // Xbox
303 auto mode = hdmiInfo.GetCurrentDisplayMode();
304 return StringUtils::Format(
305 "Desktop Resolution: {}x{} {}Bit at {:.2f}Hz", mode.ResolutionWidthInRawPixels(),
306 mode.ResolutionHeightInRawPixels(), mode.BitsPerPixel(), mode.RefreshRate());
308 else // Windows 10 UWP
310 auto info = DisplayInformation::GetForCurrentView();
311 return StringUtils::Format("Desktop Resolution: {}x{}", info.ScreenWidthInRawPixels(),
312 info.ScreenHeightInRawPixels());
314 #else
315 DEVMODE devmode = {};
316 devmode.dmSize = sizeof(devmode);
317 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devmode);
318 return StringUtils::Format("Desktop Resolution: {}x{} {}Bit at {}Hz", devmode.dmPelsWidth,
319 devmode.dmPelsHeight, devmode.dmBitsPerPel,
320 devmode.dmDisplayFrequency);
321 #endif
324 int CWIN32Util::GetDesktopColorDepth()
326 #ifdef TARGET_WINDOWS_STORE
327 CLog::LogF(LOGDEBUG, "s not implemented");
328 return 32;
329 #else
330 DEVMODE devmode = {};
331 devmode.dmSize = sizeof(devmode);
332 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devmode);
333 return (int)devmode.dmBitsPerPel;
334 #endif
337 size_t CWIN32Util::GetSystemMemorySize()
339 #ifdef TARGET_WINDOWS_STORE
340 MEMORYSTATUSEX statex = {};
341 statex.dwLength = sizeof(statex);
342 GlobalMemoryStatusEx(&statex);
343 return static_cast<size_t>(statex.ullTotalPhys / KB);
344 #else
345 ULONGLONG ramSize = 0;
346 GetPhysicallyInstalledSystemMemory(&ramSize);
347 return static_cast<size_t>(ramSize);
348 #endif
351 #ifdef TARGET_WINDOWS_DESKTOP
352 std::string CWIN32Util::GetSpecialFolder(int csidl)
354 std::string strProfilePath;
355 static const int bufSize = MAX_PATH;
356 WCHAR* buf = new WCHAR[bufSize];
358 if(SUCCEEDED(SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, buf)))
360 buf[bufSize-1] = 0;
361 g_charsetConverter.wToUTF8(buf, strProfilePath);
362 strProfilePath = UncToSmb(strProfilePath);
364 else
365 strProfilePath = "";
367 delete[] buf;
368 return strProfilePath;
370 #endif
372 std::string CWIN32Util::GetSystemPath()
374 #ifdef TARGET_WINDOWS_STORE
375 // access to system folder is not allowed in a UWP app
376 return "";
377 #else
378 return GetSpecialFolder(CSIDL_SYSTEM);
379 #endif
382 std::string CWIN32Util::GetProfilePath(const bool platformDirectories)
384 std::string strProfilePath;
385 #ifdef TARGET_WINDOWS_STORE
386 auto localFolder = ApplicationData::Current().LocalFolder();
387 strProfilePath = KODI::PLATFORM::WINDOWS::FromW(localFolder.Path().c_str());
388 #else
389 std::string strHomePath = CUtil::GetHomePath();
391 if (platformDirectories)
392 strProfilePath = URIUtils::AddFileToFolder(GetSpecialFolder(CSIDL_APPDATA|CSIDL_FLAG_CREATE), CCompileInfo::GetAppName());
393 else
394 strProfilePath = URIUtils::AddFileToFolder(strHomePath , "portable_data");
396 if (strProfilePath.length() == 0)
397 strProfilePath = strHomePath;
399 URIUtils::AddSlashAtEnd(strProfilePath);
400 #endif
401 return strProfilePath;
404 std::string CWIN32Util::UncToSmb(const std::string &strPath)
406 std::string strRetPath(strPath);
407 if(StringUtils::StartsWith(strRetPath, "\\\\"))
409 strRetPath = "smb:" + strPath;
410 StringUtils::Replace(strRetPath, '\\', '/');
412 return strRetPath;
415 std::string CWIN32Util::SmbToUnc(const std::string &strPath)
417 std::string strRetPath(strPath);
418 if(StringUtils::StartsWithNoCase(strRetPath, "smb://"))
420 StringUtils::Replace(strRetPath, "smb://", "\\\\");
421 StringUtils::Replace(strRetPath, '/', '\\');
423 return strRetPath;
426 bool CWIN32Util::AddExtraLongPathPrefix(std::wstring& path)
428 const wchar_t* const str = path.c_str();
429 if (path.length() < 4 || str[0] != L'\\' || str[1] != L'\\' || str[3] != L'\\' || str[2] != L'?')
431 path.insert(0, L"\\\\?\\");
432 return true;
434 return false;
437 bool CWIN32Util::RemoveExtraLongPathPrefix(std::wstring& path)
439 const wchar_t* const str = path.c_str();
440 if (path.length() >= 4 && str[0] == L'\\' && str[1] == L'\\' && str[3] == L'\\' && str[2] == L'?')
442 path.erase(0, 4);
443 return true;
445 return false;
448 std::wstring CWIN32Util::ConvertPathToWin32Form(const std::string& pathUtf8)
450 std::wstring result;
451 if (pathUtf8.empty())
452 return result;
454 bool convertResult;
456 if (pathUtf8.compare(0, 2, "\\\\", 2) != 0) // pathUtf8 don't start from "\\"
457 { // assume local file path in form 'C:\Folder\File.ext'
458 std::string formedPath("\\\\?\\"); // insert "\\?\" prefix
459 formedPath += URIUtils::CanonicalizePath(URIUtils::FixSlashesAndDups(pathUtf8, '\\'), '\\'); // fix duplicated and forward slashes, resolve relative path
460 convertResult = g_charsetConverter.utf8ToW(formedPath, result, false, false, true);
462 else if (pathUtf8.compare(0, 8, "\\\\?\\UNC\\", 8) == 0) // pathUtf8 starts from "\\?\UNC\"
464 std::string formedPath("\\\\?\\UNC"); // start from "\\?\UNC" prefix
465 formedPath += URIUtils::CanonicalizePath(URIUtils::FixSlashesAndDups(pathUtf8.substr(7), '\\'), '\\'); // fix duplicated and forward slashes, resolve relative path, don't touch "\\?\UNC" prefix,
466 convertResult = g_charsetConverter.utf8ToW(formedPath, result, false, false, true);
468 else if (pathUtf8.compare(0, 4, "\\\\?\\", 4) == 0) // pathUtf8 starts from "\\?\", but it's not UNC path
470 std::string formedPath("\\\\?"); // start from "\\?" prefix
471 formedPath += URIUtils::CanonicalizePath(URIUtils::FixSlashesAndDups(pathUtf8.substr(3), '\\'), '\\'); // fix duplicated and forward slashes, resolve relative path, don't touch "\\?" prefix,
472 convertResult = g_charsetConverter.utf8ToW(formedPath, result, false, false, true);
474 else // pathUtf8 starts from "\\", but not from "\\?\UNC\"
475 { // assume UNC path in form '\\server\share\folder\file.ext'
476 std::string formedPath("\\\\?\\UNC"); // append "\\?\UNC" prefix
477 formedPath += URIUtils::CanonicalizePath(URIUtils::FixSlashesAndDups(pathUtf8), '\\'); // fix duplicated and forward slashes, resolve relative path, transform "\\" prefix to single "\"
478 convertResult = g_charsetConverter.utf8ToW(formedPath, result, false, false, true);
481 if (!convertResult)
483 CLog::Log(LOGERROR, "Error converting path \"{}\" to Win32 wide string!", pathUtf8);
484 return L"";
487 return result;
490 std::wstring CWIN32Util::ConvertPathToWin32Form(const CURL& url)
492 assert(url.GetProtocol().empty() || url.IsProtocol("smb"));
494 if (url.GetFileName().empty())
495 return std::wstring(); // empty string
497 if (url.GetProtocol().empty())
499 std::wstring result;
500 if (g_charsetConverter.utf8ToW("\\\\?\\" +
501 URIUtils::CanonicalizePath(URIUtils::FixSlashesAndDups(url.GetFileName(), '\\'), '\\'), result, false, false, true))
502 return result;
504 else if (url.IsProtocol("smb"))
506 if (url.GetHostName().empty())
507 return std::wstring(); // empty string
509 std::wstring result;
510 if (g_charsetConverter.utf8ToW("\\\\?\\UNC\\" +
511 URIUtils::CanonicalizePath(URIUtils::FixSlashesAndDups(url.GetHostName() + '\\' + url.GetFileName(), '\\'), '\\'),
512 result, false, false, true))
513 return result;
515 else
516 return std::wstring(); // unsupported protocol, return empty string
518 CLog::LogF(LOGERROR, "Error converting path \"{}\" to Win32 form", url.Get());
519 return std::wstring(); // empty string
522 __time64_t CWIN32Util::fileTimeToTimeT(const FILETIME& ftimeft)
524 if (!ftimeft.dwHighDateTime && !ftimeft.dwLowDateTime)
525 return 0;
527 return fileTimeToTimeT((__int64(ftimeft.dwHighDateTime) << 32) + __int64(ftimeft.dwLowDateTime));
530 __time64_t CWIN32Util::fileTimeToTimeT(const LARGE_INTEGER& ftimeli)
532 if (ftimeli.QuadPart == 0)
533 return 0;
535 return fileTimeToTimeT(__int64(ftimeli.QuadPart));
538 HRESULT CWIN32Util::ToggleTray(const char cDriveLetter)
540 #ifdef TARGET_WINDOWS_STORE
541 CLog::LogF(LOGDEBUG, "s not implemented");
542 return false;
543 #else
544 using namespace KODI::PLATFORM::WINDOWS;
545 BOOL bRet= FALSE;
546 DWORD dwReq = 0;
547 char cDL = cDriveLetter;
548 if( !cDL )
550 std::string dvdDevice = CServiceBroker::GetMediaManager().TranslateDevicePath("");
551 if(dvdDevice == "")
552 return S_FALSE;
553 cDL = dvdDevice[0];
556 auto strVolFormat = ToW(StringUtils::Format("\\\\.\\{}:", cDL));
557 HANDLE hDrive= CreateFile( strVolFormat.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
558 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
559 auto strRootFormat = ToW(StringUtils::Format("{}:\\", cDL));
560 if( ( hDrive != INVALID_HANDLE_VALUE || GetLastError() == NO_ERROR) &&
561 ( GetDriveType( strRootFormat.c_str() ) == DRIVE_CDROM ) )
563 DWORD dwDummy;
564 dwReq = (GetDriveStatus(FromW(strVolFormat), true) == 1) ? IOCTL_STORAGE_LOAD_MEDIA : IOCTL_STORAGE_EJECT_MEDIA;
565 bRet = DeviceIoControl( hDrive, dwReq, NULL, 0, NULL, 0, &dwDummy, NULL);
567 // Windows doesn't seem to send always DBT_DEVICEREMOVECOMPLETE
568 // unmount it here too as it won't hurt
569 if(dwReq == IOCTL_STORAGE_EJECT_MEDIA && bRet == 1)
571 CMediaSource share;
572 share.strPath = StringUtils::Format("{}:", cDL);
573 share.strName = share.strPath;
574 CServiceBroker::GetMediaManager().RemoveAutoSource(share);
576 CloseHandle(hDrive);
577 return bRet? S_OK : S_FALSE;
578 #endif
581 HRESULT CWIN32Util::EjectTray(const char cDriveLetter)
583 char cDL = cDriveLetter;
584 if( !cDL )
586 std::string dvdDevice = CServiceBroker::GetMediaManager().TranslateDevicePath("");
587 if(dvdDevice.empty())
588 return S_FALSE;
589 cDL = dvdDevice[0];
592 std::string strVolFormat = StringUtils::Format("\\\\.\\{}:", cDL);
594 if(GetDriveStatus(strVolFormat, true) != 1)
595 return ToggleTray(cDL);
596 else
597 return S_OK;
600 HRESULT CWIN32Util::CloseTray(const char cDriveLetter)
602 char cDL = cDriveLetter;
603 if( !cDL )
605 std::string dvdDevice = CServiceBroker::GetMediaManager().TranslateDevicePath("");
606 if(dvdDevice.empty())
607 return S_FALSE;
608 cDL = dvdDevice[0];
611 std::string strVolFormat = StringUtils::Format("\\\\.\\{}:", cDL);
613 if(GetDriveStatus(strVolFormat, true) == 1)
614 return ToggleTray(cDL);
615 else
616 return S_OK;
619 BOOL CWIN32Util::IsCurrentUserLocalAdministrator()
621 #ifdef TARGET_WINDOWS_STORE
622 // UWP apps never run as admin
623 return false;
624 #else
625 BOOL b;
626 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
627 PSID AdministratorsGroup;
628 b = AllocateAndInitializeSid(
629 &NtAuthority,
631 SECURITY_BUILTIN_DOMAIN_RID,
632 DOMAIN_ALIAS_RID_ADMINS,
633 0, 0, 0, 0, 0, 0,
634 &AdministratorsGroup);
635 if(b)
637 if (!CheckTokenMembership( NULL, AdministratorsGroup, &b))
639 b = FALSE;
641 FreeSid(AdministratorsGroup);
644 return(b);
645 #endif
648 extern "C"
650 FILE *fopen_utf8(const char *_Filename, const char *_Mode)
652 std::string modetmp = _Mode;
653 std::wstring wfilename, wmode(modetmp.begin(), modetmp.end());
654 g_charsetConverter.utf8ToW(_Filename, wfilename, false);
655 return _wfopen(wfilename.c_str(), wmode.c_str());
659 extern "C" {
661 * Copyright (c) 1997, 1998, 2005 The NetBSD Foundation, Inc.
662 * All rights reserved.
664 * SPDX-License-Identifier: BSD-4-Clause
665 * See LICENSES/README.md for more information.
667 * Ported from NetBSD to Windows by Ron Koenderink, 2007
668 * $NetBSD: strptime.c,v 1.25 2005/11/29 03:12:00 christos Exp $
670 * This code was contributed to The NetBSD Foundation by Klaus Klein.
671 * Heavily optimised by David Laight
674 #if defined(LIBC_SCCS) && !defined(lint)
675 __RCSID("$NetBSD: strptime.c,v 1.25 2005/11/29 03:12:00 christos Exp $");
676 #endif
678 typedef unsigned char u_char;
679 typedef unsigned int uint;
680 #include <ctype.h>
681 #include <locale.h>
682 #include <string.h>
683 #include <time.h>
685 #ifdef __weak_alias
686 __weak_alias(strptime,_strptime)
687 #endif
689 #define _ctloc(x) (x)
690 const char *abday[] = {
691 "Sun", "Mon", "Tue", "Wed",
692 "Thu", "Fri", "Sat"
694 const char *day[] = {
695 "Sunday", "Monday", "Tuesday", "Wednesday",
696 "Thursday", "Friday", "Saturday"
698 const char *abmon[] = {
699 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
700 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
702 const char *mon[] = {
703 "January", "February", "March", "April", "May", "June",
704 "July", "August", "September", "October", "November", "December"
706 const char *am_pm[] = {
707 "AM", "PM"
709 const char* d_t_fmt = "%a %Ef %T %Y";
710 const char* t_fmt_ampm = "%I:%M:%S %p";
711 const char* t_fmt = "%H:%M:%S";
712 const char* d_fmt = "%m/%d/%y";
713 #define TM_YEAR_BASE 1900
714 #define __UNCONST(x) ((char*)(((const char*)(x) - (const char*)0) + (char*)0))
717 * We do not implement alternate representations. However, we always
718 * check whether a given modifier is allowed for a certain conversion.
720 #define ALT_E 0x01
721 #define ALT_O 0x02
722 #define LEGAL_ALT(x) \
724 if (alt_format & ~(x)) \
725 return NULL; \
729 static const u_char *conv_num(const unsigned char *, int *, uint, uint);
730 static const u_char *find_string(const u_char *, int *, const char * const *,
731 const char * const *, int);
734 char *
735 strptime(const char *buf, const char *fmt, struct tm *tm)
737 unsigned char c;
738 const unsigned char *bp;
739 int alt_format, i, split_year = 0;
740 const char *new_fmt;
742 bp = (const u_char *)buf;
744 while (bp != NULL && (c = *fmt++) != '\0') {
745 /* Clear `alternate' modifier prior to new conversion. */
746 alt_format = 0;
747 i = 0;
749 /* Eat up white-space. */
750 if (isspace(c)) {
751 while (isspace(*bp))
752 bp++;
753 continue;
756 if (c != '%')
757 goto literal;
760 again: switch (c = *fmt++) {
761 case '%': /* "%%" is converted to "%". */
762 literal:
763 if (c != *bp++)
764 return NULL;
765 LEGAL_ALT(0);
766 continue;
769 * "Alternative" modifiers. Just set the appropriate flag
770 * and start over again.
772 case 'E': /* "%E?" alternative conversion modifier. */
773 LEGAL_ALT(0);
774 alt_format |= ALT_E;
775 goto again;
777 case 'O': /* "%O?" alternative conversion modifier. */
778 LEGAL_ALT(0);
779 alt_format |= ALT_O;
780 goto again;
783 * "Complex" conversion rules, implemented through recursion.
785 case 'c': /* Date and time, using the locale's format. */
786 new_fmt = _ctloc(d_t_fmt);
787 goto recurse;
789 case 'D': /* The date as "%m/%d/%y". */
790 new_fmt = "%m/%d/%y";
791 LEGAL_ALT(0);
792 goto recurse;
794 case 'R': /* The time as "%H:%M". */
795 new_fmt = "%H:%M";
796 LEGAL_ALT(0);
797 goto recurse;
799 case 'r': /* The time in 12-hour clock representation. */
800 new_fmt =_ctloc(t_fmt_ampm);
801 LEGAL_ALT(0);
802 goto recurse;
804 case 'T': /* The time as "%H:%M:%S". */
805 new_fmt = "%H:%M:%S";
806 LEGAL_ALT(0);
807 goto recurse;
809 case 'X': /* The time, using the locale's format. */
810 new_fmt =_ctloc(t_fmt);
811 goto recurse;
813 case 'x': /* The date, using the locale's format. */
814 new_fmt =_ctloc(d_fmt);
815 recurse:
816 bp = (const u_char *)strptime((const char *)bp,
817 new_fmt, tm);
818 LEGAL_ALT(ALT_E);
819 continue;
822 * "Elementary" conversion rules.
824 case 'A': /* The day of week, using the locale's form. */
825 case 'a':
826 bp = find_string(bp, &tm->tm_wday, _ctloc(day),
827 _ctloc(abday), 7);
828 LEGAL_ALT(0);
829 continue;
831 case 'B': /* The month, using the locale's form. */
832 case 'b':
833 case 'h':
834 bp = find_string(bp, &tm->tm_mon, _ctloc(mon),
835 _ctloc(abmon), 12);
836 LEGAL_ALT(0);
837 continue;
839 case 'C': /* The century number. */
840 i = 20;
841 bp = conv_num(bp, &i, 0, 99);
843 i = i * 100 - TM_YEAR_BASE;
844 if (split_year)
845 i += tm->tm_year % 100;
846 split_year = 1;
847 tm->tm_year = i;
848 LEGAL_ALT(ALT_E);
849 continue;
851 case 'd': /* The day of month. */
852 case 'e':
853 bp = conv_num(bp, &tm->tm_mday, 1, 31);
854 LEGAL_ALT(ALT_O);
855 continue;
857 case 'k': /* The hour (24-hour clock representation). */
858 LEGAL_ALT(0);
859 [[fallthrough]];
860 case 'H':
861 bp = conv_num(bp, &tm->tm_hour, 0, 23);
862 LEGAL_ALT(ALT_O);
863 continue;
865 case 'l': /* The hour (12-hour clock representation). */
866 LEGAL_ALT(0);
867 [[fallthrough]];
868 case 'I':
869 bp = conv_num(bp, &tm->tm_hour, 1, 12);
870 if (tm->tm_hour == 12)
871 tm->tm_hour = 0;
872 LEGAL_ALT(ALT_O);
873 continue;
875 case 'j': /* The day of year. */
876 i = 1;
877 bp = conv_num(bp, &i, 1, 366);
878 tm->tm_yday = i - 1;
879 LEGAL_ALT(0);
880 continue;
882 case 'M': /* The minute. */
883 bp = conv_num(bp, &tm->tm_min, 0, 59);
884 LEGAL_ALT(ALT_O);
885 continue;
887 case 'm': /* The month. */
888 i = 1;
889 bp = conv_num(bp, &i, 1, 12);
890 tm->tm_mon = i - 1;
891 LEGAL_ALT(ALT_O);
892 continue;
894 case 'p': /* The locale's equivalent of AM/PM. */
895 bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2);
896 if (tm->tm_hour > 11)
897 return NULL;
898 tm->tm_hour += i * 12;
899 LEGAL_ALT(0);
900 continue;
902 case 'S': /* The seconds. */
903 bp = conv_num(bp, &tm->tm_sec, 0, 61);
904 LEGAL_ALT(ALT_O);
905 continue;
907 case 'U': /* The week of year, beginning on sunday. */
908 case 'W': /* The week of year, beginning on monday. */
910 * XXX This is bogus, as we can not assume any valid
911 * information present in the tm structure at this
912 * point to calculate a real value, so just check the
913 * range for now.
915 bp = conv_num(bp, &i, 0, 53);
916 LEGAL_ALT(ALT_O);
917 continue;
919 case 'w': /* The day of week, beginning on sunday. */
920 bp = conv_num(bp, &tm->tm_wday, 0, 6);
921 LEGAL_ALT(ALT_O);
922 continue;
924 case 'Y': /* The year. */
925 i = TM_YEAR_BASE; /* just for data sanity... */
926 bp = conv_num(bp, &i, 0, 9999);
927 tm->tm_year = i - TM_YEAR_BASE;
928 LEGAL_ALT(ALT_E);
929 continue;
931 case 'y': /* The year within 100 years of the epoch. */
932 /* LEGAL_ALT(ALT_E | ALT_O); */
933 bp = conv_num(bp, &i, 0, 99);
935 if (split_year)
936 /* preserve century */
937 i += (tm->tm_year / 100) * 100;
938 else {
939 split_year = 1;
940 if (i <= 68)
941 i = i + 2000 - TM_YEAR_BASE;
942 else
943 i = i + 1900 - TM_YEAR_BASE;
945 tm->tm_year = i;
946 continue;
949 * Miscellaneous conversions.
951 case 'n': /* Any kind of white-space. */
952 case 't':
953 while (isspace(*bp))
954 bp++;
955 LEGAL_ALT(0);
956 continue;
959 default: /* Unknown/unsupported conversion. */
960 return NULL;
964 return __UNCONST(bp);
968 static const u_char *
969 conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim)
971 uint result = 0;
972 unsigned char ch;
974 /* The limit also determines the number of valid digits. */
975 uint rulim = ulim;
977 ch = *buf;
978 if (ch < '0' || ch > '9')
979 return NULL;
981 do {
982 result *= 10;
983 result += ch - '0';
984 rulim /= 10;
985 ch = *++buf;
986 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
988 if (result < llim || result > ulim)
989 return NULL;
991 *dest = result;
992 return buf;
995 static const u_char *
996 find_string(const u_char *bp, int *tgt, const char * const *n1,
997 const char * const *n2, int c)
999 int i;
1000 size_t len;
1002 /* check full name - then abbreviated ones */
1003 for (; n1 != NULL; n1 = n2, n2 = NULL) {
1004 for (i = 0; i < c; i++, n1++) {
1005 len = strlen(*n1);
1006 if (StringUtils::CompareNoCase(*n1, (const char*)bp, len) == 0)
1008 *tgt = i;
1009 return bp + len;
1014 /* Nothing matched */
1015 return NULL;
1019 #ifdef TARGET_WINDOWS_DESKTOP
1020 LONG CWIN32Util::UtilRegGetValue( const HKEY hKey, const char *const pcKey, DWORD *const pdwType, char **const ppcBuffer, DWORD *const pdwSizeBuff, const DWORD dwSizeAdd )
1022 using KODI::PLATFORM::WINDOWS::ToW;
1024 auto pcKeyW = ToW(pcKey);
1026 DWORD dwSize;
1027 LONG lRet= RegQueryValueEx(hKey, pcKeyW.c_str(), nullptr, pdwType, nullptr, &dwSize );
1028 if (lRet == ERROR_SUCCESS)
1030 if (ppcBuffer)
1032 char *pcValue=*ppcBuffer, *pcValueTmp;
1033 if (!pcValue || !pdwSizeBuff || dwSize +dwSizeAdd > *pdwSizeBuff) {
1034 pcValueTmp = static_cast<char*>(realloc(pcValue, dwSize + dwSizeAdd));
1035 if(pcValueTmp != nullptr)
1037 pcValue = pcValueTmp;
1040 lRet= RegQueryValueEx(hKey, pcKeyW.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(pcValue), &dwSize);
1042 if ( lRet == ERROR_SUCCESS || *ppcBuffer )
1043 *ppcBuffer= pcValue;
1044 else
1045 free( pcValue );
1047 if (pdwSizeBuff) *pdwSizeBuff= dwSize +dwSizeAdd;
1049 return lRet;
1052 bool CWIN32Util::UtilRegOpenKeyEx( const HKEY hKeyParent, const char *const pcKey, const REGSAM rsAccessRights, HKEY *hKey, const bool bReadX64 )
1054 using KODI::PLATFORM::WINDOWS::ToW;
1056 const REGSAM rsAccessRightsTmp = (
1057 CSysInfo::GetKernelBitness() == 64 ? rsAccessRights |
1058 ( bReadX64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY ) : rsAccessRights );
1059 bool bRet = ( ERROR_SUCCESS == RegOpenKeyEx(hKeyParent, ToW(pcKey).c_str(), 0, rsAccessRightsTmp, hKey));
1060 return bRet;
1063 // Retrieve the filename of the process that currently has the focus.
1064 // Typically this will be some process using the system tray grabbing
1065 // the focus and causing XBMC to minimise. Logging the offending
1066 // process name can help the user fix the problem.
1067 bool CWIN32Util::GetFocussedProcess(std::string &strProcessFile)
1069 using KODI::PLATFORM::WINDOWS::FromW;
1070 strProcessFile = "";
1072 // Get the window that has the focus
1073 HWND hfocus = GetForegroundWindow();
1074 if (!hfocus)
1075 return false;
1077 // Get the process ID from the window handle
1078 DWORD pid = 0;
1079 GetWindowThreadProcessId(hfocus, &pid);
1081 // Use OpenProcess to get the process handle from the process ID
1082 HANDLE hproc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, pid);
1083 if (!hproc)
1084 return false;
1086 // Load QueryFullProcessImageName dynamically because it isn't available
1087 // in all versions of Windows.
1088 wchar_t procfile[MAX_PATH+1];
1089 DWORD procfilelen = MAX_PATH;
1091 if (QueryFullProcessImageNameW(hproc, 0, procfile, &procfilelen))
1092 strProcessFile = FromW(procfile);
1094 CloseHandle(hproc);
1096 return true;
1098 #endif
1100 // Adjust the src rectangle so that the dst is always contained in the target rectangle.
1101 void CWIN32Util::CropSource(CRect& src, CRect& dst, CRect target, UINT rotation /* = 0 */)
1103 float s_width = src.Width(), s_height = src.Height();
1104 float d_width = dst.Width(), d_height = dst.Height();
1106 if (dst.x1 < target.x1)
1108 switch (rotation)
1110 case 90:
1111 src.y1 -= (dst.x1 - target.x1) * s_height / d_width;
1112 break;
1113 case 180:
1114 src.x2 += (dst.x1 - target.x1) * s_width / d_width;
1115 break;
1116 case 270:
1117 src.y2 += (dst.x1 - target.x1) * s_height / d_width;
1118 break;
1119 default:
1120 src.x1 -= (dst.x1 - target.x1) * s_width / d_width;
1121 break;
1123 dst.x1 = target.x1;
1125 if(dst.y1 < target.y1)
1127 switch (rotation)
1129 case 90:
1130 src.x1 -= (dst.y1 - target.y1) * s_width / d_height;
1131 break;
1132 case 180:
1133 src.y2 += (dst.y1 - target.y1) * s_height / d_height;
1134 break;
1135 case 270:
1136 src.x2 += (dst.y1 - target.y1) * s_width / d_height;
1137 break;
1138 default:
1139 src.y1 -= (dst.y1 - target.y1) * s_height / d_height;
1140 break;
1142 dst.y1 = target.y1;
1144 if(dst.x2 > target.x2)
1146 switch (rotation)
1148 case 90:
1149 src.y2 -= (dst.x2 - target.x2) * s_height / d_width;
1150 break;
1151 case 180:
1152 src.x1 += (dst.x2 - target.x2) * s_width / d_width;
1153 break;
1154 case 270:
1155 src.y1 += (dst.x2 - target.x2) * s_height / d_width;
1156 break;
1157 default:
1158 src.x2 -= (dst.x2 - target.x2) * s_width / d_width;
1159 break;
1161 dst.x2 = target.x2;
1163 if(dst.y2 > target.y2)
1165 switch (rotation)
1167 case 90:
1168 src.x2 -= (dst.y2 - target.y2) * s_width / d_height;
1169 break;
1170 case 180:
1171 src.y1 += (dst.y2 - target.y2) * s_height / d_height;
1172 break;
1173 case 270:
1174 src.x1 += (dst.y2 - target.y2) * s_width / d_height;
1175 break;
1176 default:
1177 src.y2 -= (dst.y2 - target.y2) * s_height / d_height;
1178 break;
1180 dst.y2 = target.y2;
1182 // Callers expect integer coordinates.
1183 src.x1 = floor(src.x1);
1184 src.y1 = floor(src.y1);
1185 src.x2 = ceil(src.x2);
1186 src.y2 = ceil(src.y2);
1187 dst.x1 = floor(dst.x1);
1188 dst.y1 = floor(dst.y1);
1189 dst.x2 = ceil(dst.x2);
1190 dst.y2 = ceil(dst.y2);
1193 std::string CWIN32Util::WUSysMsg(DWORD dwError)
1195 #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
1196 CHAR szBuf[512];
1198 if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
1199 SS_DEFLANGID, szBuf, 511, NULL) )
1200 return StringUtils::Format("{} (0x{:X})", szBuf, dwError);
1201 else
1202 return StringUtils::Format("Unknown error (0x{:X})", dwError);
1205 bool CWIN32Util::SetThreadLocalLocale(bool enable /* = true */)
1207 const int param = enable ? _ENABLE_PER_THREAD_LOCALE : _DISABLE_PER_THREAD_LOCALE;
1208 return _configthreadlocale(param) != -1;
1211 HDR_STATUS CWIN32Util::ToggleWindowsHDR(DXGI_MODE_DESC& modeDesc)
1213 HDR_STATUS status = HDR_STATUS::HDR_TOGGLE_FAILED;
1215 #ifdef TARGET_WINDOWS_STORE
1216 auto hdmi = HdmiDisplayInformation::GetForCurrentView();
1218 if (!hdmi)
1219 return status;
1221 const auto current = hdmi.GetCurrentDisplayMode();
1223 for (const auto& mode : hdmi.GetSupportedDisplayModes())
1225 if (mode.IsSmpte2084Supported() != current.IsSmpte2084Supported() &&
1226 mode.ResolutionHeightInRawPixels() == current.ResolutionHeightInRawPixels() &&
1227 mode.ResolutionWidthInRawPixels() == current.ResolutionWidthInRawPixels() &&
1228 mode.StereoEnabled() == false &&
1229 fabs(mode.RefreshRate() - current.RefreshRate()) <= 0.00001)
1231 if (current.IsSmpte2084Supported()) // HDR is ON
1233 CLog::LogF(LOGINFO, "Toggle Windows HDR Off (ON => OFF).");
1234 if (Wait(hdmi.RequestSetCurrentDisplayModeAsync(mode, HdmiDisplayHdrOption::None)))
1235 status = HDR_STATUS::HDR_OFF;
1237 else // HDR is OFF
1239 CLog::LogF(LOGINFO, "Toggle Windows HDR On (OFF => ON).");
1240 if (Wait(hdmi.RequestSetCurrentDisplayModeAsync(mode, HdmiDisplayHdrOption::Eotf2084)))
1241 status = HDR_STATUS::HDR_ON;
1243 break;
1246 #else
1247 uint32_t pathCount = 0;
1248 uint32_t modeCount = 0;
1250 MONITORINFOEXW mi = {};
1251 mi.cbSize = sizeof(mi);
1252 GetMonitorInfoW(MonitorFromWindow(g_hWnd, MONITOR_DEFAULTTOPRIMARY), &mi);
1253 const std::wstring deviceNameW = mi.szDevice;
1255 if (ERROR_SUCCESS == GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount))
1257 std::vector<DISPLAYCONFIG_PATH_INFO> paths(pathCount);
1258 std::vector<DISPLAYCONFIG_MODE_INFO> modes(modeCount);
1260 if (ERROR_SUCCESS == QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.data(),
1261 &modeCount, modes.data(), nullptr))
1263 DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO getColorInfo = {};
1264 getColorInfo.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
1265 getColorInfo.header.size = sizeof(getColorInfo);
1267 DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE setColorState = {};
1268 setColorState.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
1269 setColorState.header.size = sizeof(setColorState);
1271 DISPLAYCONFIG_SOURCE_DEVICE_NAME getSourceName = {};
1272 getSourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
1273 getSourceName.header.size = sizeof(getSourceName);
1275 // Only try to toggle display currently used by Kodi
1276 for (const auto& path : paths)
1278 getSourceName.header.adapterId.HighPart = path.sourceInfo.adapterId.HighPart;
1279 getSourceName.header.adapterId.LowPart = path.sourceInfo.adapterId.LowPart;
1280 getSourceName.header.id = path.sourceInfo.id;
1282 if (ERROR_SUCCESS == DisplayConfigGetDeviceInfo(&getSourceName.header))
1284 const std::wstring sourceNameW = getSourceName.viewGdiDeviceName;
1285 if (deviceNameW == sourceNameW)
1287 const auto& mode = modes.at(path.targetInfo.modeInfoIdx);
1289 getColorInfo.header.adapterId.HighPart = mode.adapterId.HighPart;
1290 getColorInfo.header.adapterId.LowPart = mode.adapterId.LowPart;
1291 getColorInfo.header.id = mode.id;
1293 setColorState.header.adapterId.HighPart = mode.adapterId.HighPart;
1294 setColorState.header.adapterId.LowPart = mode.adapterId.LowPart;
1295 setColorState.header.id = mode.id;
1297 if (ERROR_SUCCESS == DisplayConfigGetDeviceInfo(&getColorInfo.header))
1299 if (getColorInfo.advancedColorSupported)
1301 if (getColorInfo.advancedColorEnabled) // HDR is ON
1303 setColorState.enableAdvancedColor = FALSE;
1304 status = HDR_STATUS::HDR_OFF;
1305 CLog::LogF(LOGINFO, "Toggle Windows HDR Off (ON => OFF).");
1307 else // HDR is OFF
1309 setColorState.enableAdvancedColor = TRUE;
1310 status = HDR_STATUS::HDR_ON;
1311 CLog::LogF(LOGINFO, "Toggle Windows HDR On (OFF => ON).");
1313 if (ERROR_SUCCESS != DisplayConfigSetDeviceInfo(&setColorState.header))
1314 status = HDR_STATUS::HDR_TOGGLE_FAILED;
1317 break;
1324 // Restores previous graphics mode before toggle HDR
1325 if (status != HDR_STATUS::HDR_TOGGLE_FAILED && modeDesc.RefreshRate.Denominator != 0)
1327 float fps = static_cast<float>(modeDesc.RefreshRate.Numerator) /
1328 static_cast<float>(modeDesc.RefreshRate.Denominator);
1329 int32_t est;
1330 DEVMODEW devmode = {};
1331 devmode.dmSize = sizeof(devmode);
1332 devmode.dmPelsWidth = modeDesc.Width;
1333 devmode.dmPelsHeight = modeDesc.Height;
1334 devmode.dmDisplayFrequency = static_cast<uint32_t>(fps);
1335 if (modeDesc.ScanlineOrdering &&
1336 modeDesc.ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE)
1337 devmode.dmDisplayFlags = DM_INTERLACED;
1338 devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
1339 est = ChangeDisplaySettingsExW(deviceNameW.c_str(), &devmode, nullptr, CDS_FULLSCREEN, nullptr);
1340 if (est == DISP_CHANGE_SUCCESSFUL)
1341 CLog::LogF(LOGDEBUG, "Previous graphics mode restored OK");
1342 else
1343 CLog::LogF(LOGERROR, "Previous graphics mode cannot be restored (error# {})", est);
1345 #endif
1347 return status;
1350 HDR_STATUS CWIN32Util::GetWindowsHDRStatus()
1352 bool advancedColorSupported = false;
1353 bool advancedColorEnabled = false;
1354 HDR_STATUS status = HDR_STATUS::HDR_UNSUPPORTED;
1356 #ifdef TARGET_WINDOWS_STORE
1357 auto displayInformation = DisplayInformation::GetForCurrentView();
1359 if (displayInformation)
1361 auto advancedColorInfo = displayInformation.GetAdvancedColorInfo();
1363 if (advancedColorInfo)
1365 if (advancedColorInfo.CurrentAdvancedColorKind() == AdvancedColorKind::HighDynamicRange)
1367 advancedColorSupported = true;
1368 advancedColorEnabled = true;
1372 // Try to find out if the display supports HDR even if Windows HDR switch is OFF
1373 if (!advancedColorEnabled)
1375 auto displayManager = DisplayManager::Create(DisplayManagerOptions::None);
1377 if (displayManager)
1379 auto targets = displayManager.GetCurrentTargets();
1381 for (const auto& target : targets)
1383 if (target.IsConnected())
1385 auto displayMonitor = target.TryGetMonitor();
1386 if (displayMonitor.MaxLuminanceInNits() >= 400.0f)
1388 advancedColorSupported = true;
1389 break;
1393 displayManager.Close();
1396 #else
1397 uint32_t pathCount = 0;
1398 uint32_t modeCount = 0;
1400 MONITORINFOEXW mi = {};
1401 mi.cbSize = sizeof(mi);
1402 GetMonitorInfoW(MonitorFromWindow(g_hWnd, MONITOR_DEFAULTTOPRIMARY), &mi);
1403 const std::wstring deviceNameW = mi.szDevice;
1405 if (ERROR_SUCCESS == GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount))
1407 std::vector<DISPLAYCONFIG_PATH_INFO> paths(pathCount);
1408 std::vector<DISPLAYCONFIG_MODE_INFO> modes(modeCount);
1410 if (ERROR_SUCCESS == QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.data(),
1411 &modeCount, modes.data(), 0))
1413 DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO getColorInfo = {};
1414 getColorInfo.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
1415 getColorInfo.header.size = sizeof(getColorInfo);
1417 DISPLAYCONFIG_SOURCE_DEVICE_NAME getSourceName = {};
1418 getSourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
1419 getSourceName.header.size = sizeof(getSourceName);
1421 for (const auto& path : paths)
1423 getSourceName.header.adapterId.HighPart = path.sourceInfo.adapterId.HighPart;
1424 getSourceName.header.adapterId.LowPart = path.sourceInfo.adapterId.LowPart;
1425 getSourceName.header.id = path.sourceInfo.id;
1427 if (ERROR_SUCCESS == DisplayConfigGetDeviceInfo(&getSourceName.header))
1429 const std::wstring sourceNameW = getSourceName.viewGdiDeviceName;
1430 if (g_hWnd == nullptr || deviceNameW == sourceNameW)
1432 const auto& mode = modes.at(path.targetInfo.modeInfoIdx);
1434 getColorInfo.header.adapterId.HighPart = mode.adapterId.HighPart;
1435 getColorInfo.header.adapterId.LowPart = mode.adapterId.LowPart;
1436 getColorInfo.header.id = mode.id;
1438 if (ERROR_SUCCESS == DisplayConfigGetDeviceInfo(&getColorInfo.header))
1440 if (getColorInfo.advancedColorEnabled)
1441 advancedColorEnabled = true;
1443 if (getColorInfo.advancedColorSupported)
1444 advancedColorSupported = true;
1447 if (g_hWnd != nullptr)
1448 break;
1454 #endif
1456 if (!advancedColorSupported)
1458 status = HDR_STATUS::HDR_UNSUPPORTED;
1459 if (CServiceBroker::IsServiceManagerUp())
1460 CLog::LogF(LOGDEBUG, "Display is not HDR capable or cannot be detected");
1462 else
1464 status = advancedColorEnabled ? HDR_STATUS::HDR_ON : HDR_STATUS::HDR_OFF;
1465 if (CServiceBroker::IsServiceManagerUp())
1466 CLog::LogF(LOGDEBUG, "Display is HDR capable and current HDR status is {}",
1467 advancedColorEnabled ? "ON" : "OFF");
1470 return status;
1474 * \brief Retrieve from the system the max luminance of SDR content in HDR.
1476 * Retrieve from the system the max luminance of SDR content in HDR.
1477 * Note: always returns 80 nits when the screen is in SDR mode.
1479 * \param gdiDeviceName The screen to retrieve the information for
1480 * \param sdrWhiteLevel Max luminance in nits, clamped to 10000
1481 * \return true if a value could be read from the system and copied to sdrWhiteLevel, false otherwise
1483 bool CWIN32Util::GetSystemSdrWhiteLevel(const std::wstring& gdiDeviceName, float* sdrWhiteLevel)
1485 #ifdef TARGET_WINDOWS_STORE
1486 auto displayInformation = DisplayInformation::GetForCurrentView();
1488 if (displayInformation)
1490 auto advancedColorInfo = displayInformation.GetAdvancedColorInfo();
1492 if (advancedColorInfo)
1494 const float sdrNits = advancedColorInfo.SdrWhiteLevelInNits();
1495 if (sdrWhiteLevel)
1497 if (sdrNits > 10000.0f)
1498 *sdrWhiteLevel = 10000.0f;
1499 else
1500 *sdrWhiteLevel = sdrNits;
1502 return true;
1505 return false;
1506 #else
1507 // DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL was added in Windows 10 1709
1509 uint32_t pathCount{0};
1510 uint32_t modeCount{0};
1511 std::vector<DISPLAYCONFIG_PATH_INFO> paths;
1512 std::vector<DISPLAYCONFIG_MODE_INFO> modes;
1514 uint32_t flags = QDC_ONLY_ACTIVE_PATHS;
1515 LONG result = ERROR_SUCCESS;
1519 if (GetDisplayConfigBufferSizes(flags, &pathCount, &modeCount) != ERROR_SUCCESS)
1520 return false;
1522 paths.resize(pathCount);
1523 modes.resize(modeCount);
1525 result = QueryDisplayConfig(flags, &pathCount, paths.data(), &modeCount, modes.data(), nullptr);
1526 } while (result == ERROR_INSUFFICIENT_BUFFER);
1528 if (result == ERROR_SUCCESS)
1530 paths.resize(pathCount);
1531 modes.resize(modeCount);
1533 for (const auto& path : paths)
1535 DISPLAYCONFIG_SOURCE_DEVICE_NAME source{};
1536 source.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
1537 source.header.size = sizeof(source);
1538 source.header.adapterId = path.sourceInfo.adapterId;
1539 source.header.id = path.sourceInfo.id;
1541 if (DisplayConfigGetDeviceInfo(&source.header) == ERROR_SUCCESS)
1543 if (gdiDeviceName == source.viewGdiDeviceName)
1545 DISPLAYCONFIG_SDR_WHITE_LEVEL config{};
1546 config.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
1547 config.header.size = sizeof(config);
1548 config.header.adapterId = path.targetInfo.adapterId;
1549 config.header.id = path.targetInfo.id;
1550 if (DisplayConfigGetDeviceInfo(&config.header) == ERROR_SUCCESS)
1552 if (sdrWhiteLevel)
1554 const float sdrNits = static_cast<const float>(config.SDRWhiteLevel * 80 / 1000);
1555 if (sdrNits > 10000.0f)
1556 *sdrWhiteLevel = 10000.0f;
1557 else
1558 *sdrWhiteLevel = sdrNits;
1560 return true;
1562 break;
1567 return (false);
1568 #endif
1571 void CWIN32Util::PlatformSyslog()
1573 CLog::Log(LOGINFO, "System has {:.1f} GB of RAM installed",
1574 GetSystemMemorySize() / static_cast<double>(MB));
1575 CLog::Log(LOGINFO, "{}", GetResInfoString());
1576 CLog::Log(LOGINFO, "Running with {} rights",
1577 (IsCurrentUserLocalAdministrator() == TRUE) ? "administrator" : "restricted");
1578 CLog::Log(LOGINFO, "Aero is {}", (g_sysinfo.IsAeroDisabled() == true) ? "disabled" : "enabled");
1579 HDR_STATUS hdrStatus = GetWindowsHDRStatus();
1580 if (hdrStatus == HDR_STATUS::HDR_UNSUPPORTED)
1581 CLog::Log(LOGINFO, "Display is not HDR capable or cannot be detected");
1582 else
1583 CLog::Log(LOGINFO, "Display HDR capable is detected and Windows HDR switch is {}",
1584 (hdrStatus == HDR_STATUS::HDR_ON) ? "ON" : "OFF");
1587 VideoDriverInfo CWIN32Util::GetVideoDriverInfo(const UINT vendorId, const std::wstring& driverDesc)
1589 VideoDriverInfo info = {};
1591 #ifdef TARGET_WINDOWS_DESKTOP
1592 HKEY hKey = nullptr;
1593 const wchar_t* SUBKEY = L"SYSTEM\\CurrentControlSet\\Control\\Video";
1595 if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_LOCAL_MACHINE, SUBKEY, 0, KEY_ENUMERATE_SUB_KEYS, &hKey))
1596 return {};
1598 LSTATUS sta = ERROR_SUCCESS;
1599 wchar_t keyName[128] = {};
1600 DWORD index = 0;
1601 DWORD len;
1603 using KODI::PLATFORM::WINDOWS::FromW;
1607 len = sizeof(keyName) / sizeof(wchar_t);
1608 sta = RegEnumKeyExW(hKey, index, keyName, &len, nullptr, nullptr, nullptr, nullptr);
1609 index++;
1611 if (sta != ERROR_SUCCESS)
1612 continue;
1614 std::wstring subkey(SUBKEY);
1615 subkey.append(L"\\");
1616 subkey.append(keyName);
1617 subkey.append(L"\\");
1618 subkey.append(L"0000");
1619 DWORD lg;
1620 wchar_t desc[128] = {};
1621 lg = sizeof(desc);
1622 if (ERROR_SUCCESS != RegGetValueW(HKEY_LOCAL_MACHINE, subkey.c_str(), L"DriverDesc",
1623 RRF_RT_REG_SZ, nullptr, desc, &lg))
1624 continue;
1626 std::wstring s_desc(desc);
1627 if (s_desc != driverDesc)
1628 continue;
1630 // driver of interest found, we read version
1631 wchar_t wversion[64] = {};
1632 lg = sizeof(wversion);
1633 if (ERROR_SUCCESS != RegGetValueW(HKEY_LOCAL_MACHINE, subkey.c_str(), L"DriverVersion",
1634 RRF_RT_REG_SZ, nullptr, wversion, &lg))
1635 continue;
1637 const std::string version = FromW(std::wstring(wversion));
1639 info = FormatVideoDriverInfo(vendorId, version);
1641 } while (sta == ERROR_SUCCESS && !info.valid);
1643 RegCloseKey(hKey);
1644 #endif
1646 return info;
1649 VideoDriverInfo CWIN32Util::GetVideoDriverInfoDX(const UINT vendorId, LUID adapterLuid)
1651 VideoDriverInfo info = {};
1653 #ifdef TARGET_WINDOWS_DESKTOP
1654 HKEY hKey = nullptr;
1655 const wchar_t* SUBKEY = L"SOFTWARE\\Microsoft\\DirectX";
1657 if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_LOCAL_MACHINE, SUBKEY, 0, KEY_ENUMERATE_SUB_KEYS, &hKey))
1658 return {};
1660 LSTATUS sta = ERROR_SUCCESS;
1661 wchar_t keyName[128] = {};
1662 DWORD index = 0;
1663 DWORD len;
1665 using KODI::PLATFORM::WINDOWS::FromW;
1669 len = sizeof(keyName) / sizeof(wchar_t);
1670 sta = RegEnumKeyExW(hKey, index, keyName, &len, nullptr, nullptr, nullptr, nullptr);
1671 index++;
1673 if (sta != ERROR_SUCCESS)
1674 continue;
1676 LUID luid = {};
1677 DWORD qwordSize = sizeof(luid);
1679 if (ERROR_SUCCESS !=
1680 RegGetValueW(hKey, keyName, L"AdapterLuid", RRF_RT_QWORD, nullptr, &luid, &qwordSize))
1681 continue;
1683 if (luid.HighPart != adapterLuid.HighPart || luid.LowPart != adapterLuid.LowPart)
1684 continue;
1686 // driver of interest found, read the version
1687 uint64_t rawDriverVersion{};
1688 if (ERROR_SUCCESS != RegGetValueW(hKey, keyName, L"DriverVersion", RRF_RT_QWORD, nullptr,
1689 &rawDriverVersion, &qwordSize))
1690 continue;
1692 info = FormatVideoDriverInfo(vendorId, rawDriverVersion);
1694 } while (sta == ERROR_SUCCESS && !info.valid);
1696 RegCloseKey(hKey);
1697 #endif
1699 return info;
1702 VideoDriverInfo CWIN32Util::FormatVideoDriverInfo(const UINT vendorId, uint64_t rawVersion)
1704 const unsigned int part1 = static_cast<unsigned int>(rawVersion >> 48);
1705 const unsigned int part2 = static_cast<unsigned int>((rawVersion >> 32) & 0xFFFF);
1706 const unsigned int part3 = static_cast<unsigned int>((rawVersion >> 16) & 0xFFFF);
1707 const unsigned int part4 = static_cast<unsigned int>(rawVersion & 0xFFFF);
1709 std::ostringstream ss;
1710 ss << part1 << '.' << part2 << '.' << part3 << '.' << part4;
1712 return FormatVideoDriverInfo(vendorId, ss.str());
1715 VideoDriverInfo CWIN32Util::FormatVideoDriverInfo(const UINT vendorId, const std::string version)
1717 VideoDriverInfo info = {};
1719 info.valid = true;
1720 info.vendorId = vendorId;
1721 info.version = version;
1723 // convert driver store version to Nvidia version
1724 if (vendorId == PCIV_NVIDIA)
1726 std::string ver(version);
1727 StringUtils::Replace(ver, ".", "");
1728 info.majorVersion = std::stoi(ver.substr(ver.length() - 5, 3));
1729 info.minorVersion = std::stoi(ver.substr(ver.length() - 2, 2));
1731 else // for Intel/AMD fill major version only
1733 info.majorVersion = std::stoi(version.substr(0, version.find('.')));
1735 return info;
1738 std::wstring CWIN32Util::GetDisplayFriendlyName(const std::wstring& gdiDeviceName)
1740 #ifdef TARGET_WINDOWS_STORE
1741 // Not supported
1742 return std::wstring();
1743 #else
1745 uint32_t pathCount{};
1746 uint32_t modeCount{};
1747 std::vector<DISPLAYCONFIG_PATH_INFO> paths;
1748 std::vector<DISPLAYCONFIG_MODE_INFO> modes;
1750 uint32_t flags = QDC_ONLY_ACTIVE_PATHS;
1751 LONG result = ERROR_SUCCESS;
1755 if (GetDisplayConfigBufferSizes(flags, &pathCount, &modeCount) != ERROR_SUCCESS)
1756 return std::wstring();
1758 paths.resize(pathCount);
1759 modes.resize(modeCount);
1761 result = QueryDisplayConfig(flags, &pathCount, paths.data(), &modeCount, modes.data(), nullptr);
1762 } while (result == ERROR_INSUFFICIENT_BUFFER);
1764 if (result == ERROR_SUCCESS)
1766 paths.resize(pathCount);
1767 modes.resize(modeCount);
1769 for (const auto& path : paths)
1771 DISPLAYCONFIG_SOURCE_DEVICE_NAME source = {};
1772 source.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
1773 source.header.size = sizeof(source);
1774 source.header.adapterId = path.sourceInfo.adapterId;
1775 source.header.id = path.sourceInfo.id;
1777 if (DisplayConfigGetDeviceInfo(&source.header) == ERROR_SUCCESS)
1779 if (gdiDeviceName == source.viewGdiDeviceName)
1781 DISPLAYCONFIG_TARGET_DEVICE_NAME target = {};
1782 target.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
1783 target.header.size = sizeof(target);
1784 target.header.adapterId = path.targetInfo.adapterId;
1785 target.header.id = path.targetInfo.id;
1786 if (DisplayConfigGetDeviceInfo(&target.header) == ERROR_SUCCESS)
1787 return target.monitorFriendlyDeviceName;
1789 break;
1794 return std::wstring();
1795 #endif
1798 using SETTHREADDESCRIPTION = HRESULT(WINAPI*)(HANDLE hThread, PCWSTR lpThreadDescription);
1800 bool CWIN32Util::SetThreadName(const HANDLE handle, const std::string& name)
1802 #if defined(TARGET_WINDOWS_STORE)
1803 //not supported
1804 return false;
1805 #else
1806 static bool initialized = false;
1807 static HINSTANCE hinstLib = NULL;
1808 static SETTHREADDESCRIPTION pSetThreadDescription = nullptr;
1810 if (!initialized)
1812 initialized = true;
1814 // MS documentation: SetThreadDescription available since Windows 10 1607
1815 // function located in Kernel32.dll
1816 // except for Windows 10 1607, where it is located in KernelBase.dll
1817 CSysInfo::WindowsVersion winver = CSysInfo::GetWindowsVersion();
1819 if (winver < CSysInfo::WindowsVersion::WindowsVersionWin10_1607)
1820 return false;
1821 else if (winver == CSysInfo::WindowsVersion::WindowsVersionWin10_1607)
1822 hinstLib = LoadLibrary(L"KernelBase.dll");
1823 else if (winver > CSysInfo::WindowsVersion::WindowsVersionWin10_1607)
1824 hinstLib = LoadLibrary(L"Kernel32.dll");
1826 if (hinstLib != NULL)
1828 pSetThreadDescription = reinterpret_cast<SETTHREADDESCRIPTION>(
1829 ::GetProcAddress(hinstLib, "SetThreadDescription"));
1832 if (pSetThreadDescription == nullptr && hinstLib)
1833 FreeLibrary(hinstLib);
1836 if (pSetThreadDescription != nullptr &&
1837 SUCCEEDED(pSetThreadDescription(handle, KODI::PLATFORM::WINDOWS::ToW(name).c_str())))
1838 return true;
1839 else
1840 return false;
1842 #endif
1845 static std::array<int, 4> ParseVideoDriverInfo(const std::string& version)
1847 std::array<int, 4> result{};
1849 // the string is destroyed in the process, make a copy first.
1850 std::string v{version};
1852 char* p = std::strtok(v.data(), ".");
1853 for (int idx = 0; p && idx < 4; ++idx)
1855 result[idx] = std::stoi(p);
1856 p = std::strtok(NULL, ".");
1859 return result;
1862 bool CWIN32Util::IsDriverVersionAtLeast(const std::string& version1, const std::string& version2)
1864 const std::array<int, 4> v1 = ParseVideoDriverInfo(version1);
1865 const std::array<int, 4> v2 = ParseVideoDriverInfo(version2);
1867 for (int idx = 0; idx < 4; ++idx)
1869 if (v1[idx] > v2[idx])
1870 return true;
1871 else if (v1[idx] < v2[idx])
1872 return false;
1873 // equality: compare the next segment.
1875 return true;