Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / vcl / opengl / win / WinDeviceInfo.cxx
blob9b4438cbccdb3f032a85130fbdc33d8451efb3cd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <opengl/win/WinDeviceInfo.hxx>
12 #include <opengl/win/blocklist_parser.hxx>
13 #include <config_folders.h>
15 #if !defined WIN32_LEAN_AND_MEAN
16 # define WIN32_LEAN_AND_MEAN
17 #endif
18 #include <windows.h>
19 #include <objbase.h>
20 #include <setupapi.h>
21 #include <algorithm>
22 #include <cstdint>
23 #include <memory>
25 #include <osl/file.hxx>
26 #include <rtl/bootstrap.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <tools/stream.hxx>
29 #include <o3tl/char16_t2wchar_t.hxx>
31 #include <desktop/crashreport.hxx>
33 OUString* WinOpenGLDeviceInfo::mpDeviceVendors[wgl::DeviceVendorMax];
34 std::vector<wgl::DriverInfo> WinOpenGLDeviceInfo::maDriverInfo;
36 namespace {
39 * Compute the length of an array with constant length. (Use of this method
40 * with a non-array pointer will not compile.)
42 * Beware of the implicit trailing '\0' when using this with string constants.
44 template<typename T, size_t N>
45 size_t ArrayLength(T (&)[N])
47 return N;
50 #define GFX_DRIVER_VERSION(a,b,c,d) \
51 ((uint64_t(a)<<48) | (uint64_t(b)<<32) | (uint64_t(c)<<16) | uint64_t(d))
53 bool GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName, OUString& destString, int type)
55 HKEY key;
56 DWORD dwcbData;
57 DWORD dValue;
58 DWORD resultType;
59 LONG result;
60 bool retval = true;
62 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyLocation, 0, KEY_QUERY_VALUE, &key);
63 if (result != ERROR_SUCCESS)
65 return false;
68 switch (type)
70 case REG_DWORD:
72 // We only use this for vram size
73 dwcbData = sizeof(dValue);
74 result = RegQueryValueExW(key, keyName, nullptr, &resultType,
75 reinterpret_cast<LPBYTE>(&dValue), &dwcbData);
76 if (result == ERROR_SUCCESS && resultType == REG_DWORD)
78 dValue = dValue / 1024 / 1024;
79 destString += OUString::number(int32_t(dValue));
81 else
83 retval = false;
85 break;
87 case REG_MULTI_SZ:
89 // A chain of null-separated strings; we convert the nulls to spaces
90 WCHAR wCharValue[1024];
91 dwcbData = sizeof(wCharValue);
93 result = RegQueryValueExW(key, keyName, nullptr, &resultType,
94 reinterpret_cast<LPBYTE>(wCharValue), &dwcbData);
95 if (result == ERROR_SUCCESS && resultType == REG_MULTI_SZ)
97 // This bit here could probably be cleaner.
98 bool isValid = false;
100 DWORD strLen = dwcbData/sizeof(wCharValue[0]);
101 for (DWORD i = 0; i < strLen; i++)
103 if (wCharValue[i] == '\0')
105 if (i < strLen - 1 && wCharValue[i + 1] == '\0')
107 isValid = true;
108 break;
110 else
112 wCharValue[i] = ' ';
117 // ensure wCharValue is null terminated
118 wCharValue[strLen-1] = '\0';
120 if (isValid)
121 destString = OUString(o3tl::toU(wCharValue));
124 else
126 retval = false;
129 break;
132 RegCloseKey(key);
134 return retval;
137 // The device ID is a string like PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD
138 // this function is used to extract the id's out of it
139 uint32_t ParseIDFromDeviceID(const OUString &key, const char *prefix, int length)
141 OUString id = key.toAsciiUpperCase();
142 OUString aPrefix = OUString::fromUtf8(prefix);
143 int32_t start = id.indexOf(aPrefix);
144 if (start != -1)
146 id = id.copy(start + aPrefix.getLength(), length);
148 return id.toUInt32(16);
151 // OS version in 16.16 major/minor form
152 // based on http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
153 enum {
154 kWindowsUnknown = 0,
155 kWindowsXP = 0x00050001,
156 kWindowsServer2003 = 0x00050002,
157 kWindowsVista = 0x00060000,
158 kWindows7 = 0x00060001,
159 kWindows8 = 0x00060002,
160 kWindows8_1 = 0x00060003,
161 kWindows10 = 0x000A0000 // Major 10 Minor 0
165 wgl::OperatingSystem WindowsVersionToOperatingSystem(int32_t aWindowsVersion)
167 switch(aWindowsVersion)
169 case kWindowsXP:
170 return wgl::DRIVER_OS_WINDOWS_XP;
171 case kWindowsServer2003:
172 return wgl::DRIVER_OS_WINDOWS_SERVER_2003;
173 case kWindowsVista:
174 return wgl::DRIVER_OS_WINDOWS_VISTA;
175 case kWindows7:
176 return wgl::DRIVER_OS_WINDOWS_7;
177 case kWindows8:
178 return wgl::DRIVER_OS_WINDOWS_8;
179 case kWindows8_1:
180 return wgl::DRIVER_OS_WINDOWS_8_1;
181 case kWindows10:
182 return wgl::DRIVER_OS_WINDOWS_10;
183 case kWindowsUnknown:
184 default:
185 return wgl::DRIVER_OS_UNKNOWN;
190 int32_t WindowsOSVersion()
192 static int32_t winVersion = kWindowsUnknown;
194 if (winVersion == kWindowsUnknown)
196 // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
197 // subject to manifest-based behavior since Windows 8.1, so give wrong results.
198 // Another approach would be to use NetWkstaGetInfo, but that has some small
199 // reported delays (some milliseconds), and might get slower in domains with
200 // poor network connections.
201 // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
202 HINSTANCE hLibrary = LoadLibraryW(L"kernel32.dll");
203 if (hLibrary != nullptr)
205 wchar_t szPath[MAX_PATH];
206 DWORD dwCount = GetModuleFileNameW(hLibrary, szPath, SAL_N_ELEMENTS(szPath));
207 FreeLibrary(hLibrary);
208 if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
210 dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
211 if (dwCount != 0)
213 std::unique_ptr<char> ver(new char[dwCount]);
214 if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
216 void* pBlock = nullptr;
217 UINT dwBlockSz = 0;
218 if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
220 VS_FIXEDFILEINFO *vinfo = static_cast<VS_FIXEDFILEINFO *>(pBlock);
221 winVersion = int32_t(vinfo->dwProductVersionMS);
229 return winVersion;
232 // This allows us to pad driver version 'substrings' with 0s, this
233 // effectively allows us to treat the version numbers as 'decimals'. This is
234 // a little strange but this method seems to do the right thing for all
235 // different vendor's driver strings. i.e. .98 will become 9800, which is
236 // larger than .978 which would become 9780.
237 void PadDriverDecimal(char *aString)
239 for (int i = 0; i < 4; i++)
241 if (!aString[i])
243 for (int c = i; c < 4; c++)
245 aString[c] = '0';
247 break;
250 aString[4] = 0;
253 // All destination string storage needs to have at least 5 bytes available.
254 bool SplitDriverVersion(const char *aSource, char *aAStr, char *aBStr, char *aCStr, char *aDStr)
256 // sscanf doesn't do what we want here to we parse this manually.
257 int len = strlen(aSource);
258 char *dest[4] = { aAStr, aBStr, aCStr, aDStr };
259 unsigned destIdx = 0;
260 unsigned destPos = 0;
262 for (int i = 0; i < len; i++)
264 if (destIdx >= ArrayLength(dest))
266 // Invalid format found. Ensure we don't access dest beyond bounds.
267 return false;
270 if (aSource[i] == '.')
272 dest[destIdx++][destPos] = 0;
273 destPos = 0;
274 continue;
277 if (destPos > 3)
279 // Ignore more than 4 chars. Ensure we never access dest[destIdx]
280 // beyond its bounds.
281 continue;
284 dest[destIdx][destPos++] = aSource[i];
287 // Add last terminator.
288 dest[destIdx][destPos] = 0;
290 if (destIdx != ArrayLength(dest) - 1)
292 return false;
294 return true;
297 /* Other interesting places for info:
298 * IDXGIAdapter::GetDesc()
299 * IDirectDraw7::GetAvailableVidMem()
300 * e->GetAvailableTextureMem()
301 * */
303 template<typename T> void appendIntegerWithPadding(OUString& rString, T value, sal_uInt32 nChars)
305 rString += "0x";
306 OUString aValue = OUString::number(value, 16);
307 sal_Int32 nLength = aValue.getLength();
308 sal_uInt32 nPadLength = nChars - nLength;
309 assert(nPadLength >= 0);
310 OUStringBuffer aBuffer;
311 for (sal_uInt32 i = 0; i < nPadLength; ++i)
313 aBuffer.append("0");
315 rString += aBuffer.makeStringAndClear() + aValue;
318 #define DEVICE_KEY_PREFIX L"\\Registry\\Machine\\"
321 namespace wgl {
323 bool ParseDriverVersion(const OUString& aVersion, uint64_t& rNumericVersion)
325 rNumericVersion = 0;
327 #if defined(_WIN32)
328 int a, b, c, d;
329 char aStr[8], bStr[8], cStr[8], dStr[8];
330 /* honestly, why do I even bother */
331 OString aOVersion = OUStringToOString(aVersion, RTL_TEXTENCODING_UTF8);
332 if (!SplitDriverVersion(aOVersion.getStr(), aStr, bStr, cStr, dStr))
333 return false;
335 PadDriverDecimal(bStr);
336 PadDriverDecimal(cStr);
337 PadDriverDecimal(dStr);
339 a = atoi(aStr);
340 b = atoi(bStr);
341 c = atoi(cStr);
342 d = atoi(dStr);
344 if (a < 0 || a > 0xffff) return false;
345 if (b < 0 || b > 0xffff) return false;
346 if (c < 0 || c > 0xffff) return false;
347 if (d < 0 || d > 0xffff) return false;
349 rNumericVersion = GFX_DRIVER_VERSION(a, b, c, d);
350 return true;
351 #else
352 return false;
353 #endif
356 uint64_t DriverInfo::allDriverVersions = ~(uint64_t(0));
358 DriverInfo::DriverInfo()
359 : meOperatingSystem(wgl::DRIVER_OS_UNKNOWN),
360 mnOperatingSystemVersion(0),
361 maAdapterVendor(WinOpenGLDeviceInfo::GetDeviceVendor(VendorAll)),
362 mbWhitelisted(false),
363 meComparisonOp(DRIVER_COMPARISON_IGNORED),
364 mnDriverVersion(0),
365 mnDriverVersionMax(0)
368 DriverInfo::DriverInfo(OperatingSystem os, const OUString& vendor,
369 VersionComparisonOp op,
370 uint64_t driverVersion,
371 bool bWhitelisted,
372 const char *suggestedVersion /* = nullptr */)
373 : meOperatingSystem(os),
374 mnOperatingSystemVersion(0),
375 maAdapterVendor(vendor),
376 mbWhitelisted(bWhitelisted),
377 meComparisonOp(op),
378 mnDriverVersion(driverVersion),
379 mnDriverVersionMax(0)
381 if (suggestedVersion)
382 maSuggestedVersion = OStringToOUString(OString(suggestedVersion), RTL_TEXTENCODING_UTF8);
385 DriverInfo::~DriverInfo()
391 WinOpenGLDeviceInfo::WinOpenGLDeviceInfo():
392 mbHasDualGPU(false),
393 mbRDP(false)
395 GetData();
396 FillBlacklist();
399 WinOpenGLDeviceInfo::~WinOpenGLDeviceInfo()
403 namespace {
405 struct compareIgnoreAsciiCase
407 explicit compareIgnoreAsciiCase(const OUString& rString)
408 : maString(rString)
412 bool operator()(const OUString& rCompare)
414 return maString.equalsIgnoreAsciiCase(rCompare);
417 private:
418 OUString maString;
423 bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList(std::vector<wgl::DriverInfo>& aDeviceInfos,
424 OUString const & sDriverVersion, OUString const & sAdapterVendorID,
425 OUString const & sAdapterDeviceID, uint32_t nWindowsVersion)
427 uint64_t driverVersion;
428 wgl::ParseDriverVersion(sDriverVersion, driverVersion);
430 wgl::OperatingSystem eOS = WindowsVersionToOperatingSystem(nWindowsVersion);
431 bool match = false;
432 for (std::vector<wgl::DriverInfo>::size_type i = 0; i < aDeviceInfos.size(); i++)
434 if (aDeviceInfos[i].meOperatingSystem != wgl::DRIVER_OS_ALL &&
435 aDeviceInfos[i].meOperatingSystem != eOS)
437 continue;
440 if (aDeviceInfos[i].mnOperatingSystemVersion && aDeviceInfos[i].mnOperatingSystemVersion != nWindowsVersion)
442 continue;
445 if (!aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(GetDeviceVendor(wgl::VendorAll)) &&
446 !aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(sAdapterVendorID))
448 continue;
451 if (std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(), compareIgnoreAsciiCase("all")) &&
452 std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(), compareIgnoreAsciiCase(sAdapterDeviceID)))
454 continue;
457 switch (aDeviceInfos[i].meComparisonOp)
459 case wgl::DRIVER_LESS_THAN:
460 match = driverVersion < aDeviceInfos[i].mnDriverVersion;
461 break;
462 case wgl::DRIVER_LESS_THAN_OR_EQUAL:
463 match = driverVersion <= aDeviceInfos[i].mnDriverVersion;
464 break;
465 case wgl::DRIVER_GREATER_THAN:
466 match = driverVersion > aDeviceInfos[i].mnDriverVersion;
467 break;
468 case wgl::DRIVER_GREATER_THAN_OR_EQUAL:
469 match = driverVersion >= aDeviceInfos[i].mnDriverVersion;
470 break;
471 case wgl::DRIVER_EQUAL:
472 match = driverVersion == aDeviceInfos[i].mnDriverVersion;
473 break;
474 case wgl::DRIVER_NOT_EQUAL:
475 match = driverVersion != aDeviceInfos[i].mnDriverVersion;
476 break;
477 case wgl::DRIVER_BETWEEN_EXCLUSIVE:
478 match = driverVersion > aDeviceInfos[i].mnDriverVersion && driverVersion < aDeviceInfos[i].mnDriverVersionMax;
479 break;
480 case wgl::DRIVER_BETWEEN_INCLUSIVE:
481 match = driverVersion >= aDeviceInfos[i].mnDriverVersion && driverVersion <= aDeviceInfos[i].mnDriverVersionMax;
482 break;
483 case wgl::DRIVER_BETWEEN_INCLUSIVE_START:
484 match = driverVersion >= aDeviceInfos[i].mnDriverVersion && driverVersion < aDeviceInfos[i].mnDriverVersionMax;
485 break;
486 case wgl::DRIVER_COMPARISON_IGNORED:
487 // We don't have a comparison op, so we match everything.
488 match = true;
489 break;
490 default:
491 SAL_WARN("vcl.opengl", "Bogus op in GfxDriverInfo");
492 break;
495 if (match || aDeviceInfos[i].mnDriverVersion == wgl::DriverInfo::allDriverVersions)
497 // white listed drivers
498 if (aDeviceInfos[i].mbWhitelisted)
500 SAL_WARN("vcl.opengl", "whitelisted driver");
501 return false;
504 match = true;
505 SAL_WARN("vcl.opengl", "use : " << aDeviceInfos[i].maSuggestedVersion);
506 break;
510 SAL_INFO("vcl.opengl", (match ? "BLACKLISTED" : "not blacklisted"));
511 return match;
514 bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList()
516 return FindBlocklistedDeviceInList(maDriverInfo, maDriverVersion, maAdapterVendorID, maAdapterDeviceID, mnWindowsVersion);
519 namespace {
521 OUString getCacheFolder()
523 OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/");
524 rtl::Bootstrap::expandMacros(url);
526 osl::Directory::create(url);
528 return url;
531 void writeToLog(SvStream& rStrm, const char* pKey, const OUString & rVal)
533 rStrm.WriteCharPtr(pKey);
534 rStrm.WriteCharPtr(": ");
535 rStrm.WriteOString(OUStringToOString(rVal, RTL_TEXTENCODING_UTF8));
536 rStrm.WriteChar('\n');
541 bool WinOpenGLDeviceInfo::isDeviceBlocked()
543 CrashReporter::AddKeyValue("OpenGLVendor", maAdapterVendorID);
544 CrashReporter::AddKeyValue("OpenGLDevice", maAdapterDeviceID);
545 CrashReporter::AddKeyValue("OpenGLDriver", maDriverVersion);
547 SAL_INFO("vcl.opengl", maDriverVersion);
548 SAL_INFO("vcl.opengl", maDriverDate);
549 SAL_INFO("vcl.opengl", maDeviceID);
550 SAL_INFO("vcl.opengl", maAdapterVendorID);
551 SAL_INFO("vcl.opengl", maAdapterDeviceID);
552 SAL_INFO("vcl.opengl", maAdapterSubsysID);
553 SAL_INFO("vcl.opengl", maDeviceKey);
554 SAL_INFO("vcl.opengl", maDeviceString);
556 OUString aCacheFolder = getCacheFolder();
558 OUString aCacheFile(aCacheFolder + "/opengl_device.log");
559 SvFileStream aOpenGLLogFile(aCacheFile, StreamMode::WRITE);
561 writeToLog(aOpenGLLogFile, "DriverVersion", maDriverVersion);
562 writeToLog(aOpenGLLogFile, "DriverDate", maDriverDate);
563 writeToLog(aOpenGLLogFile, "DeviceID", maDeviceID);
564 writeToLog(aOpenGLLogFile, "AdapterVendorID", maAdapterVendorID);
565 writeToLog(aOpenGLLogFile, "AdapterDeviceID", maAdapterDeviceID);
566 writeToLog(aOpenGLLogFile, "AdapterSubsysID", maAdapterSubsysID);
567 writeToLog(aOpenGLLogFile, "DeviceKey", maDeviceKey);
568 writeToLog(aOpenGLLogFile, "DeviceString", maDeviceString);
570 // Check if the device is blocked from the downloaded blocklist. If not, check
571 // the static list after that. This order is used so that we can later escape
572 // out of static blocks (i.e. if we were wrong or something was patched, we
573 // can back out our static block without doing a release).
574 if (mbRDP)
576 SAL_WARN("vcl.opengl", "all OpenGL blocked for RDP sessions");
577 return true;
580 return FindBlocklistedDeviceInList();
583 void WinOpenGLDeviceInfo::GetData()
585 DISPLAY_DEVICEW displayDevice;
586 displayDevice.cb = sizeof(displayDevice);
588 mnWindowsVersion = WindowsOSVersion();
589 int deviceIndex = 0;
591 while (EnumDisplayDevicesW(nullptr, deviceIndex, &displayDevice, 0))
593 if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
595 break;
597 deviceIndex++;
600 // make sure the string is null terminated
601 // (using the term "null" here to mean a zero UTF-16 unit)
602 if (wcsnlen(displayDevice.DeviceKey, ArrayLength(displayDevice.DeviceKey))
603 == ArrayLength(displayDevice.DeviceKey))
605 // we did not find a null
606 SAL_WARN("vcl.opengl", "string not null terminated");
607 return;
610 /* DeviceKey is "reserved" according to MSDN so we'll be careful with it */
611 /* check that DeviceKey begins with DEVICE_KEY_PREFIX */
612 /* some systems have a DeviceKey starting with \REGISTRY\Machine\ so we need to compare case insensitively */
613 if (_wcsnicmp(displayDevice.DeviceKey, DEVICE_KEY_PREFIX, ArrayLength(DEVICE_KEY_PREFIX)-1) != 0)
615 SAL_WARN("vcl.opengl", "incorrect DeviceKey");
616 return;
619 // chop off DEVICE_KEY_PREFIX
620 maDeviceKey = o3tl::toU(displayDevice.DeviceKey) + ArrayLength(DEVICE_KEY_PREFIX)-1;
622 maDeviceID = o3tl::toU(displayDevice.DeviceID);
623 maDeviceString = o3tl::toU(displayDevice.DeviceString);
625 if (maDeviceID.isEmpty() &&
626 (maDeviceString == "RDPDD Chained DD" ||
627 (maDeviceString == "RDPUDD Chained DD")))
629 // we need to block RDP as it does not provide OpenGL 2.1+
630 mbRDP = true;
631 SAL_WARN("vcl.opengl", "RDP => blocked");
632 return;
635 /* create a device information set composed of the current display device */
636 HDEVINFO devinfo = SetupDiGetClassDevsW(nullptr, o3tl::toW(maDeviceID.getStr()), nullptr,
637 DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
639 if (devinfo != INVALID_HANDLE_VALUE)
641 HKEY key;
642 LONG result;
643 WCHAR value[255];
644 DWORD dwcbData;
645 SP_DEVINFO_DATA devinfoData;
646 DWORD memberIndex = 0;
648 devinfoData.cbSize = sizeof(devinfoData);
649 /* enumerate device information elements in the device information set */
650 while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
652 /* get a string that identifies the device's driver key */
653 if (SetupDiGetDeviceRegistryPropertyW(devinfo,
654 &devinfoData,
655 SPDRP_DRIVER,
656 nullptr,
657 reinterpret_cast<PBYTE>(value),
658 sizeof(value),
659 nullptr))
661 OUString driverKey("System\\CurrentControlSet\\Control\\Class\\");
662 driverKey += o3tl::toU(value);
663 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, o3tl::toW(driverKey.getStr()), 0, KEY_QUERY_VALUE, &key);
664 if (result == ERROR_SUCCESS)
666 /* we've found the driver we're looking for */
667 dwcbData = sizeof(value);
668 result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
669 reinterpret_cast<LPBYTE>(value), &dwcbData);
670 if (result == ERROR_SUCCESS)
672 maDriverVersion = OUString(o3tl::toU(value));
674 else
676 // If the entry wasn't found, assume the worst (0.0.0.0).
677 maDriverVersion = OUString("0.0.0.0");
679 dwcbData = sizeof(value);
680 result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
681 reinterpret_cast<LPBYTE>(value), &dwcbData);
682 if (result == ERROR_SUCCESS)
684 maDriverDate = o3tl::toU(value);
686 else
688 // Again, assume the worst
689 maDriverDate = OUString("01-01-1970");
691 RegCloseKey(key);
692 break;
697 SetupDiDestroyDeviceInfoList(devinfo);
699 else
701 SAL_WARN("vcl.opengl", "invalid handle value");
704 appendIntegerWithPadding(maAdapterVendorID, ParseIDFromDeviceID(maDeviceID, "VEN_", 4), 4);
705 appendIntegerWithPadding(maAdapterDeviceID, ParseIDFromDeviceID(maDeviceID, "&DEV_", 4), 4);
706 appendIntegerWithPadding(maAdapterSubsysID, ParseIDFromDeviceID(maDeviceID, "&SUBSYS_", 8), 8);
708 // We now check for second display adapter.
710 // Device interface class for display adapters.
711 CLSID GUID_DISPLAY_DEVICE_ARRIVAL;
712 HRESULT hresult = CLSIDFromString(L"{1CA05180-A699-450A-9A0C-DE4FBE3DDD89}",
713 &GUID_DISPLAY_DEVICE_ARRIVAL);
714 if (hresult == NOERROR)
716 devinfo = SetupDiGetClassDevsW(&GUID_DISPLAY_DEVICE_ARRIVAL,
717 nullptr, nullptr,
718 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
720 if (devinfo != INVALID_HANDLE_VALUE)
722 HKEY key;
723 LONG result;
724 WCHAR value[255];
725 DWORD dwcbData;
726 SP_DEVINFO_DATA devinfoData;
727 DWORD memberIndex = 0;
728 devinfoData.cbSize = sizeof(devinfoData);
730 OUString aAdapterDriver2;
731 OUString aDeviceID2;
732 OUString aDriverVersion2;
733 OUString aDriverDate2;
734 uint32_t adapterVendorID2;
735 uint32_t adapterDeviceID2;
737 /* enumerate device information elements in the device information set */
738 while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
740 /* get a string that identifies the device's driver key */
741 if (SetupDiGetDeviceRegistryPropertyW(devinfo,
742 &devinfoData,
743 SPDRP_DRIVER,
744 nullptr,
745 reinterpret_cast<PBYTE>(value),
746 sizeof(value),
747 nullptr))
749 OUString driverKey2("System\\CurrentControlSet\\Control\\Class\\");
750 driverKey2 += o3tl::toU(value);
751 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, o3tl::toW(driverKey2.getStr()), 0, KEY_QUERY_VALUE, &key);
752 if (result == ERROR_SUCCESS)
754 dwcbData = sizeof(value);
755 result = RegQueryValueExW(key, L"MatchingDeviceId", nullptr,
756 nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData);
757 if (result != ERROR_SUCCESS)
759 continue;
761 aDeviceID2 = o3tl::toU(value);
762 OUString aAdapterVendorID2String;
763 OUString aAdapterDeviceID2String;
764 adapterVendorID2 = ParseIDFromDeviceID(aDeviceID2, "VEN_", 4);
765 appendIntegerWithPadding(aAdapterVendorID2String, adapterVendorID2, 4);
766 adapterDeviceID2 = ParseIDFromDeviceID(aDeviceID2, "&DEV_", 4);
767 appendIntegerWithPadding(aAdapterDeviceID2String, adapterDeviceID2, 4);
768 if (maAdapterVendorID == aAdapterVendorID2String &&
769 maAdapterDeviceID == aAdapterDeviceID2String)
771 RegCloseKey(key);
772 continue;
775 // If this device is missing driver information, it is unlikely to
776 // be a real display adapter.
777 if (!GetKeyValue(o3tl::toW(driverKey2.getStr()), L"InstalledDisplayDrivers",
778 aAdapterDriver2, REG_MULTI_SZ))
780 RegCloseKey(key);
781 continue;
783 dwcbData = sizeof(value);
784 result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
785 reinterpret_cast<LPBYTE>(value), &dwcbData);
786 if (result != ERROR_SUCCESS)
788 RegCloseKey(key);
789 continue;
791 aDriverVersion2 = o3tl::toU(value);
792 dwcbData = sizeof(value);
793 result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
794 reinterpret_cast<LPBYTE>(value), &dwcbData);
795 if (result != ERROR_SUCCESS)
797 RegCloseKey(key);
798 continue;
800 aDriverDate2 = o3tl::toU(value);
801 dwcbData = sizeof(value);
802 result = RegQueryValueExW(key, L"Device Description", nullptr,
803 nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData);
804 if (result != ERROR_SUCCESS)
806 dwcbData = sizeof(value);
807 result = RegQueryValueExW(key, L"DriverDesc", nullptr, nullptr,
808 reinterpret_cast<LPBYTE>(value), &dwcbData);
810 RegCloseKey(key);
811 if (result == ERROR_SUCCESS)
813 mbHasDualGPU = true;
814 maDeviceString2 = o3tl::toU(value);
815 maDeviceID2 = aDeviceID2;
816 maDeviceKey2 = driverKey2;
817 maDriverVersion2 = aDriverVersion2;
818 maDriverDate2 = aDriverDate2;
819 appendIntegerWithPadding(maAdapterVendorID2, adapterVendorID2, 4);
820 appendIntegerWithPadding(maAdapterDeviceID2, adapterDeviceID2, 4);
821 appendIntegerWithPadding(maAdapterSubsysID2, ParseIDFromDeviceID(maDeviceID2, "&SUBSYS_", 8), 8);
822 break;
828 SetupDiDestroyDeviceInfoList(devinfo);
833 // Macro for assigning a device vendor id to a string.
834 #define DECLARE_VENDOR_ID(name, deviceId) \
835 case name: \
836 *mpDeviceVendors[id] = deviceId; \
837 break;
839 OUString WinOpenGLDeviceInfo::GetDeviceVendor(wgl::DeviceVendor id)
841 assert(id >= 0 && id < wgl::DeviceVendorMax);
843 if (mpDeviceVendors[id])
844 return *mpDeviceVendors[id];
846 mpDeviceVendors[id] = new OUString();
848 switch (id)
850 DECLARE_VENDOR_ID(wgl::VendorAll, "");
851 DECLARE_VENDOR_ID(wgl::VendorIntel, "0x8086");
852 DECLARE_VENDOR_ID(wgl::VendorNVIDIA, "0x10de");
853 DECLARE_VENDOR_ID(wgl::VendorAMD, "0x1022");
854 DECLARE_VENDOR_ID(wgl::VendorATI, "0x1002");
855 DECLARE_VENDOR_ID(wgl::VendorMicrosoft, "0x1414");
856 // Suppress a warning.
857 DECLARE_VENDOR_ID(wgl::DeviceVendorMax, "");
860 return *mpDeviceVendors[id];
863 namespace {
866 OUString getBlacklistFile()
868 OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER);
869 rtl::Bootstrap::expandMacros(url);
871 return url + "/opengl/opengl_blacklist_windows.xml";
877 void WinOpenGLDeviceInfo::FillBlacklist()
879 OUString aURL = getBlacklistFile();
880 WinBlocklistParser aParser(aURL, maDriverInfo);
881 try {
882 aParser.parse();
884 catch (...)
886 SAL_WARN("vcl.opengl", "error parsing blacklist");
887 maDriverInfo.clear();
892 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */