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.
11 #include "CompileInfo.h"
12 #include "ServiceBroker.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"
30 #ifdef TARGET_WINDOWS_DESKTOP
41 #ifdef TARGET_WINDOWS_DESKTOP
45 using namespace MEDIA_DETECT
;
47 #ifdef TARGET_WINDOWS_STORE
48 #include "platform/win10/AsyncHelpers.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
;
64 void VideoDriverInfo::Log()
68 CLog::LogF(LOGERROR
, "video driver version information is not valid");
72 if (vendorId
== PCIV_NVIDIA
)
73 CLog::LogF(LOGINFO
, "video driver version is {} {}.{} ({})", DX::GetGFXProviderName(vendorId
),
74 majorVersion
, minorVersion
, version
);
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());
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
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
114 if (hDevice
== INVALID_HANDLE_VALUE
) // cannot open the drive
116 CLog::LogF(LOGERROR
, "Failed to CreateFile for {}.", strPath
);
120 CLog::LogF(LOGDEBUG
, "Requesting media status for drive {}.", strPath
);
121 iResult
= DeviceIoControl((HANDLE
) hDevice
, // handle to device
122 IOCTL_STORAGE_CHECK_VERIFY2
, // dwIoControlCode
125 &ulChanges
, // lpOutBuffer
126 sizeof(ULONG
), // nOutBufferSize
127 &dwBytesReturned
, // number of bytes returned
128 NULL
); // OVERLAPPED structure
130 CloseHandle(hDevice
);
135 // don't request the tray status as we often doesn't need it
139 hDevice
= CreateFile( strPathW
.c_str(),
140 GENERIC_READ
| GENERIC_WRITE
,
141 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
144 FILE_ATTRIBUTE_READONLY
,
147 if (hDevice
== INVALID_HANDLE_VALUE
)
149 CLog::LogF(LOGERROR
, "Failed to CreateFile2 for {}.", strPath
);
153 sptd_sb
.sptd
.Length
=sizeof(SCSI_PASS_THROUGH_DIRECT
);
154 sptd_sb
.sptd
.PathId
=0;
155 sptd_sb
.sptd
.TargetId
=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
),
191 CloseHandle(hDevice
);
196 if(DataBuf
[5] == 0) // tray close
198 else if(DataBuf
[5] == 1) // tray open
201 return 2; // tray closed, media present
203 CLog::LogF(LOGERROR
, "Could not determine tray status {}", GetLastError());
208 char CWIN32Util::FirstDriveFromMask (ULONG unitmask
)
211 for (i
= 0; i
< 26; ++i
)
213 if (unitmask
& 0x1) break;
214 unitmask
= unitmask
>> 1;
219 bool CWIN32Util::XBMCShellExecute(const std::string
&strPath
, bool bWaitForScriptExit
)
221 #ifdef TARGET_WINDOWS_STORE
222 CLog::LogF(LOGDEBUG
, "s not implemented");
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())
235 size_t iIndex
= std::string::npos
;
237 if (strCommand
[0] == '\"')
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
);
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
);
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());
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
);
324 int CWIN32Util::GetDesktopColorDepth()
326 #ifdef TARGET_WINDOWS_STORE
327 CLog::LogF(LOGDEBUG
, "s not implemented");
330 DEVMODE devmode
= {};
331 devmode
.dmSize
= sizeof(devmode
);
332 EnumDisplaySettings(NULL
, ENUM_CURRENT_SETTINGS
, &devmode
);
333 return (int)devmode
.dmBitsPerPel
;
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
);
345 ULONGLONG ramSize
= 0;
346 GetPhysicallyInstalledSystemMemory(&ramSize
);
347 return static_cast<size_t>(ramSize
);
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
)))
361 g_charsetConverter
.wToUTF8(buf
, strProfilePath
);
362 strProfilePath
= UncToSmb(strProfilePath
);
368 return strProfilePath
;
372 std::string
CWIN32Util::GetSystemPath()
374 #ifdef TARGET_WINDOWS_STORE
375 // access to system folder is not allowed in a UWP app
378 return GetSpecialFolder(CSIDL_SYSTEM
);
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());
389 std::string strHomePath
= CUtil::GetHomePath();
391 if (platformDirectories
)
392 strProfilePath
= URIUtils::AddFileToFolder(GetSpecialFolder(CSIDL_APPDATA
|CSIDL_FLAG_CREATE
), CCompileInfo::GetAppName());
394 strProfilePath
= URIUtils::AddFileToFolder(strHomePath
, "portable_data");
396 if (strProfilePath
.length() == 0)
397 strProfilePath
= strHomePath
;
399 URIUtils::AddSlashAtEnd(strProfilePath
);
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
, '\\', '/');
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
, '/', '\\');
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
"\\\\?\\");
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
'?')
448 std::wstring
CWIN32Util::ConvertPathToWin32Form(const std::string
& pathUtf8
)
451 if (pathUtf8
.empty())
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);
483 CLog::Log(LOGERROR
, "Error converting path \"{}\" to Win32 wide string!", pathUtf8
);
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())
500 if (g_charsetConverter
.utf8ToW("\\\\?\\" +
501 URIUtils::CanonicalizePath(URIUtils::FixSlashesAndDups(url
.GetFileName(), '\\'), '\\'), result
, false, false, true))
504 else if (url
.IsProtocol("smb"))
506 if (url
.GetHostName().empty())
507 return std::wstring(); // empty string
510 if (g_charsetConverter
.utf8ToW("\\\\?\\UNC\\" +
511 URIUtils::CanonicalizePath(URIUtils::FixSlashesAndDups(url
.GetHostName() + '\\' + url
.GetFileName(), '\\'), '\\'),
512 result
, false, false, true))
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
)
527 return fileTimeToTimeT((__int64(ftimeft
.dwHighDateTime
) << 32) + __int64(ftimeft
.dwLowDateTime
));
530 __time64_t
CWIN32Util::fileTimeToTimeT(const LARGE_INTEGER
& ftimeli
)
532 if (ftimeli
.QuadPart
== 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");
544 using namespace KODI::PLATFORM::WINDOWS
;
547 char cDL
= cDriveLetter
;
550 std::string dvdDevice
= CServiceBroker::GetMediaManager().TranslateDevicePath("");
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
) )
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)
572 share
.strPath
= StringUtils::Format("{}:", cDL
);
573 share
.strName
= share
.strPath
;
574 CServiceBroker::GetMediaManager().RemoveAutoSource(share
);
577 return bRet
? S_OK
: S_FALSE
;
581 HRESULT
CWIN32Util::EjectTray(const char cDriveLetter
)
583 char cDL
= cDriveLetter
;
586 std::string dvdDevice
= CServiceBroker::GetMediaManager().TranslateDevicePath("");
587 if(dvdDevice
.empty())
592 std::string strVolFormat
= StringUtils::Format("\\\\.\\{}:", cDL
);
594 if(GetDriveStatus(strVolFormat
, true) != 1)
595 return ToggleTray(cDL
);
600 HRESULT
CWIN32Util::CloseTray(const char cDriveLetter
)
602 char cDL
= cDriveLetter
;
605 std::string dvdDevice
= CServiceBroker::GetMediaManager().TranslateDevicePath("");
606 if(dvdDevice
.empty())
611 std::string strVolFormat
= StringUtils::Format("\\\\.\\{}:", cDL
);
613 if(GetDriveStatus(strVolFormat
, true) == 1)
614 return ToggleTray(cDL
);
619 BOOL
CWIN32Util::IsCurrentUserLocalAdministrator()
621 #ifdef TARGET_WINDOWS_STORE
622 // UWP apps never run as admin
626 SID_IDENTIFIER_AUTHORITY NtAuthority
= SECURITY_NT_AUTHORITY
;
627 PSID AdministratorsGroup
;
628 b
= AllocateAndInitializeSid(
631 SECURITY_BUILTIN_DOMAIN_RID
,
632 DOMAIN_ALIAS_RID_ADMINS
,
634 &AdministratorsGroup
);
637 if (!CheckTokenMembership( NULL
, AdministratorsGroup
, &b
))
641 FreeSid(AdministratorsGroup
);
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());
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 $");
678 typedef unsigned char u_char
;
679 typedef unsigned int uint
;
686 __weak_alias(strptime
,_strptime
)
689 #define _ctloc(x) (x)
690 const char *abday
[] = {
691 "Sun", "Mon", "Tue", "Wed",
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
[] = {
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.
722 #define LEGAL_ALT(x) \
724 if (alt_format & ~(x)) \
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);
735 strptime(const char *buf
, const char *fmt
, struct tm
*tm
)
738 const unsigned char *bp
;
739 int alt_format
, i
, split_year
= 0;
742 bp
= (const u_char
*)buf
;
744 while (bp
!= NULL
&& (c
= *fmt
++) != '\0') {
745 /* Clear `alternate' modifier prior to new conversion. */
749 /* Eat up white-space. */
760 again
: switch (c
= *fmt
++) {
761 case '%': /* "%%" is converted to "%". */
769 * "Alternative" modifiers. Just set the appropriate flag
770 * and start over again.
772 case 'E': /* "%E?" alternative conversion modifier. */
777 case 'O': /* "%O?" alternative conversion modifier. */
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
);
789 case 'D': /* The date as "%m/%d/%y". */
790 new_fmt
= "%m/%d/%y";
794 case 'R': /* The time as "%H:%M". */
799 case 'r': /* The time in 12-hour clock representation. */
800 new_fmt
=_ctloc(t_fmt_ampm
);
804 case 'T': /* The time as "%H:%M:%S". */
805 new_fmt
= "%H:%M:%S";
809 case 'X': /* The time, using the locale's format. */
810 new_fmt
=_ctloc(t_fmt
);
813 case 'x': /* The date, using the locale's format. */
814 new_fmt
=_ctloc(d_fmt
);
816 bp
= (const u_char
*)strptime((const char *)bp
,
822 * "Elementary" conversion rules.
824 case 'A': /* The day of week, using the locale's form. */
826 bp
= find_string(bp
, &tm
->tm_wday
, _ctloc(day
),
831 case 'B': /* The month, using the locale's form. */
834 bp
= find_string(bp
, &tm
->tm_mon
, _ctloc(mon
),
839 case 'C': /* The century number. */
841 bp
= conv_num(bp
, &i
, 0, 99);
843 i
= i
* 100 - TM_YEAR_BASE
;
845 i
+= tm
->tm_year
% 100;
851 case 'd': /* The day of month. */
853 bp
= conv_num(bp
, &tm
->tm_mday
, 1, 31);
857 case 'k': /* The hour (24-hour clock representation). */
861 bp
= conv_num(bp
, &tm
->tm_hour
, 0, 23);
865 case 'l': /* The hour (12-hour clock representation). */
869 bp
= conv_num(bp
, &tm
->tm_hour
, 1, 12);
870 if (tm
->tm_hour
== 12)
875 case 'j': /* The day of year. */
877 bp
= conv_num(bp
, &i
, 1, 366);
882 case 'M': /* The minute. */
883 bp
= conv_num(bp
, &tm
->tm_min
, 0, 59);
887 case 'm': /* The month. */
889 bp
= conv_num(bp
, &i
, 1, 12);
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)
898 tm
->tm_hour
+= i
* 12;
902 case 'S': /* The seconds. */
903 bp
= conv_num(bp
, &tm
->tm_sec
, 0, 61);
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
915 bp
= conv_num(bp
, &i
, 0, 53);
919 case 'w': /* The day of week, beginning on sunday. */
920 bp
= conv_num(bp
, &tm
->tm_wday
, 0, 6);
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
;
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);
936 /* preserve century */
937 i
+= (tm
->tm_year
/ 100) * 100;
941 i
= i
+ 2000 - TM_YEAR_BASE
;
943 i
= i
+ 1900 - TM_YEAR_BASE
;
949 * Miscellaneous conversions.
951 case 'n': /* Any kind of white-space. */
959 default: /* Unknown/unsupported conversion. */
964 return __UNCONST(bp
);
968 static const u_char
*
969 conv_num(const unsigned char *buf
, int *dest
, uint llim
, uint ulim
)
974 /* The limit also determines the number of valid digits. */
978 if (ch
< '0' || ch
> '9')
986 } while ((result
* 10 <= ulim
) && rulim
&& ch
>= '0' && ch
<= '9');
988 if (result
< llim
|| result
> ulim
)
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
)
1002 /* check full name - then abbreviated ones */
1003 for (; n1
!= NULL
; n1
= n2
, n2
= NULL
) {
1004 for (i
= 0; i
< c
; i
++, n1
++) {
1006 if (StringUtils::CompareNoCase(*n1
, (const char*)bp
, len
) == 0)
1014 /* Nothing matched */
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
);
1027 LONG lRet
= RegQueryValueEx(hKey
, pcKeyW
.c_str(), nullptr, pdwType
, nullptr, &dwSize
);
1028 if (lRet
== ERROR_SUCCESS
)
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
;
1047 if (pdwSizeBuff
) *pdwSizeBuff
= dwSize
+dwSizeAdd
;
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
));
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();
1077 // Get the process ID from the window handle
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
);
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
);
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
)
1111 src
.y1
-= (dst
.x1
- target
.x1
) * s_height
/ d_width
;
1114 src
.x2
+= (dst
.x1
- target
.x1
) * s_width
/ d_width
;
1117 src
.y2
+= (dst
.x1
- target
.x1
) * s_height
/ d_width
;
1120 src
.x1
-= (dst
.x1
- target
.x1
) * s_width
/ d_width
;
1125 if(dst
.y1
< target
.y1
)
1130 src
.x1
-= (dst
.y1
- target
.y1
) * s_width
/ d_height
;
1133 src
.y2
+= (dst
.y1
- target
.y1
) * s_height
/ d_height
;
1136 src
.x2
+= (dst
.y1
- target
.y1
) * s_width
/ d_height
;
1139 src
.y1
-= (dst
.y1
- target
.y1
) * s_height
/ d_height
;
1144 if(dst
.x2
> target
.x2
)
1149 src
.y2
-= (dst
.x2
- target
.x2
) * s_height
/ d_width
;
1152 src
.x1
+= (dst
.x2
- target
.x2
) * s_width
/ d_width
;
1155 src
.y1
+= (dst
.x2
- target
.x2
) * s_height
/ d_width
;
1158 src
.x2
-= (dst
.x2
- target
.x2
) * s_width
/ d_width
;
1163 if(dst
.y2
> target
.y2
)
1168 src
.x2
-= (dst
.y2
- target
.y2
) * s_width
/ d_height
;
1171 src
.y1
+= (dst
.y2
- target
.y2
) * s_height
/ d_height
;
1174 src
.x1
+= (dst
.y2
- target
.y2
) * s_width
/ d_height
;
1177 src
.y2
-= (dst
.y2
- target
.y2
) * s_height
/ d_height
;
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)
1198 if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
, NULL
, dwError
,
1199 SS_DEFLANGID
, szBuf
, 511, NULL
) )
1200 return StringUtils::Format("{} (0x{:X})", szBuf
, dwError
);
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();
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
;
1239 CLog::LogF(LOGINFO
, "Toggle Windows HDR On (OFF => ON).");
1240 if (Wait(hdmi
.RequestSetCurrentDisplayModeAsync(mode
, HdmiDisplayHdrOption::Eotf2084
)))
1241 status
= HDR_STATUS::HDR_ON
;
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).");
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
;
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
);
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");
1343 CLog::LogF(LOGERROR
, "Previous graphics mode cannot be restored (error# {})", est
);
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
);
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;
1393 displayManager
.Close();
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)
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");
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");
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();
1497 if (sdrNits
> 10000.0f
)
1498 *sdrWhiteLevel
= 10000.0f
;
1500 *sdrWhiteLevel
= sdrNits
;
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
)
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
)
1554 const float sdrNits
= static_cast<const float>(config
.SDRWhiteLevel
* 80 / 1000);
1555 if (sdrNits
> 10000.0f
)
1556 *sdrWhiteLevel
= 10000.0f
;
1558 *sdrWhiteLevel
= sdrNits
;
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");
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
))
1598 LSTATUS sta
= ERROR_SUCCESS
;
1599 wchar_t keyName
[128] = {};
1603 using KODI::PLATFORM::WINDOWS::FromW
;
1607 len
= sizeof(keyName
) / sizeof(wchar_t);
1608 sta
= RegEnumKeyExW(hKey
, index
, keyName
, &len
, nullptr, nullptr, nullptr, nullptr);
1611 if (sta
!= ERROR_SUCCESS
)
1614 std::wstring
subkey(SUBKEY
);
1615 subkey
.append(L
"\\");
1616 subkey
.append(keyName
);
1617 subkey
.append(L
"\\");
1618 subkey
.append(L
"0000");
1620 wchar_t desc
[128] = {};
1622 if (ERROR_SUCCESS
!= RegGetValueW(HKEY_LOCAL_MACHINE
, subkey
.c_str(), L
"DriverDesc",
1623 RRF_RT_REG_SZ
, nullptr, desc
, &lg
))
1626 std::wstring
s_desc(desc
);
1627 if (s_desc
!= driverDesc
)
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
))
1637 const std::string version
= FromW(std::wstring(wversion
));
1639 info
= FormatVideoDriverInfo(vendorId
, version
);
1641 } while (sta
== ERROR_SUCCESS
&& !info
.valid
);
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
))
1660 LSTATUS sta
= ERROR_SUCCESS
;
1661 wchar_t keyName
[128] = {};
1665 using KODI::PLATFORM::WINDOWS::FromW
;
1669 len
= sizeof(keyName
) / sizeof(wchar_t);
1670 sta
= RegEnumKeyExW(hKey
, index
, keyName
, &len
, nullptr, nullptr, nullptr, nullptr);
1673 if (sta
!= ERROR_SUCCESS
)
1677 DWORD qwordSize
= sizeof(luid
);
1679 if (ERROR_SUCCESS
!=
1680 RegGetValueW(hKey
, keyName
, L
"AdapterLuid", RRF_RT_QWORD
, nullptr, &luid
, &qwordSize
))
1683 if (luid
.HighPart
!= adapterLuid
.HighPart
|| luid
.LowPart
!= adapterLuid
.LowPart
)
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
))
1692 info
= FormatVideoDriverInfo(vendorId
, rawDriverVersion
);
1694 } while (sta
== ERROR_SUCCESS
&& !info
.valid
);
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
= {};
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('.')));
1738 std::wstring
CWIN32Util::GetDisplayFriendlyName(const std::wstring
& gdiDeviceName
)
1740 #ifdef TARGET_WINDOWS_STORE
1742 return std::wstring();
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
;
1794 return std::wstring();
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)
1806 static bool initialized
= false;
1807 static HINSTANCE hinstLib
= NULL
;
1808 static SETTHREADDESCRIPTION pSetThreadDescription
= nullptr;
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
)
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())))
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
, ".");
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
])
1871 else if (v1
[idx
] < v2
[idx
])
1873 // equality: compare the next segment.