Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / opengl / win / WinDeviceInfo.cxx
blob7dc85bcfddd7f7231ffdc254b2fc72fc3de91e58
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 <sal/log.hxx>
29 #include <tools/stream.hxx>
30 #include <o3tl/char16_t2wchar_t.hxx>
32 #include <desktop/crashreport.hxx>
34 OUString* WinOpenGLDeviceInfo::mpDeviceVendors[wgl::DeviceVendorMax];
35 std::vector<wgl::DriverInfo> WinOpenGLDeviceInfo::maDriverInfo;
37 namespace {
40 * Compute the length of an array with constant length. (Use of this method
41 * with a non-array pointer will not compile.)
43 * Beware of the implicit trailing '\0' when using this with string constants.
45 template<typename T, size_t N>
46 size_t ArrayLength(T (&)[N])
48 return N;
51 #define GFX_DRIVER_VERSION(a,b,c,d) \
52 ((uint64_t(a)<<48) | (uint64_t(b)<<32) | (uint64_t(c)<<16) | uint64_t(d))
54 bool GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName, OUString& destString, int type)
56 HKEY key;
57 DWORD dwcbData;
58 DWORD dValue;
59 DWORD resultType;
60 LONG result;
61 bool retval = true;
63 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyLocation, 0, KEY_QUERY_VALUE, &key);
64 if (result != ERROR_SUCCESS)
66 return false;
69 switch (type)
71 case REG_DWORD:
73 // We only use this for vram size
74 dwcbData = sizeof(dValue);
75 result = RegQueryValueExW(key, keyName, nullptr, &resultType,
76 reinterpret_cast<LPBYTE>(&dValue), &dwcbData);
77 if (result == ERROR_SUCCESS && resultType == REG_DWORD)
79 dValue = dValue / 1024 / 1024;
80 destString += OUString::number(int32_t(dValue));
82 else
84 retval = false;
86 break;
88 case REG_MULTI_SZ:
90 // A chain of null-separated strings; we convert the nulls to spaces
91 WCHAR wCharValue[1024];
92 dwcbData = sizeof(wCharValue);
94 result = RegQueryValueExW(key, keyName, nullptr, &resultType,
95 reinterpret_cast<LPBYTE>(wCharValue), &dwcbData);
96 if (result == ERROR_SUCCESS && resultType == REG_MULTI_SZ)
98 // This bit here could probably be cleaner.
99 bool isValid = false;
101 DWORD strLen = dwcbData/sizeof(wCharValue[0]);
102 for (DWORD i = 0; i < strLen; i++)
104 if (wCharValue[i] == '\0')
106 if (i < strLen - 1 && wCharValue[i + 1] == '\0')
108 isValid = true;
109 break;
111 else
113 wCharValue[i] = ' ';
118 // ensure wCharValue is null terminated
119 wCharValue[strLen-1] = '\0';
121 if (isValid)
122 destString = OUString(o3tl::toU(wCharValue));
125 else
127 retval = false;
130 break;
133 RegCloseKey(key);
135 return retval;
138 // The device ID is a string like PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD
139 // this function is used to extract the id's out of it
140 uint32_t ParseIDFromDeviceID(const OUString &key, const char *prefix, int length)
142 OUString id = key.toAsciiUpperCase();
143 OUString aPrefix = OUString::fromUtf8(prefix);
144 int32_t start = id.indexOf(aPrefix);
145 if (start != -1)
147 id = id.copy(start + aPrefix.getLength(), length);
149 return id.toUInt32(16);
152 // OS version in 16.16 major/minor form
153 // based on http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
154 enum {
155 kWindowsUnknown = 0,
156 kWindows7 = 0x00060001,
157 kWindows8 = 0x00060002,
158 kWindows8_1 = 0x00060003,
159 kWindows10 = 0x000A0000 // Major 10 Minor 0
163 wgl::OperatingSystem WindowsVersionToOperatingSystem(int32_t aWindowsVersion)
165 switch(aWindowsVersion)
167 case kWindows7:
168 return wgl::DRIVER_OS_WINDOWS_7;
169 case kWindows8:
170 return wgl::DRIVER_OS_WINDOWS_8;
171 case kWindows8_1:
172 return wgl::DRIVER_OS_WINDOWS_8_1;
173 case kWindows10:
174 return wgl::DRIVER_OS_WINDOWS_10;
175 case kWindowsUnknown:
176 default:
177 return wgl::DRIVER_OS_UNKNOWN;
182 int32_t WindowsOSVersion()
184 static int32_t winVersion = [&]()
186 // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
187 // subject to manifest-based behavior since Windows 8.1, so give wrong results.
188 // Another approach would be to use NetWkstaGetInfo, but that has some small
189 // reported delays (some milliseconds), and might get slower in domains with
190 // poor network connections.
191 // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
192 HINSTANCE hLibrary = LoadLibraryW(L"kernel32.dll");
193 if (hLibrary != nullptr)
195 wchar_t szPath[MAX_PATH];
196 DWORD dwCount = GetModuleFileNameW(hLibrary, szPath, SAL_N_ELEMENTS(szPath));
197 FreeLibrary(hLibrary);
198 if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
200 dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
201 if (dwCount != 0)
203 std::unique_ptr<char[]> ver(new char[dwCount]);
204 if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
206 void* pBlock = nullptr;
207 UINT dwBlockSz = 0;
208 if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
210 VS_FIXEDFILEINFO *vinfo = static_cast<VS_FIXEDFILEINFO *>(pBlock);
211 return int32_t(vinfo->dwProductVersionMS);
217 return int32_t(kWindowsUnknown);
218 }();
220 return winVersion;
223 // This allows us to pad driver version 'substrings' with 0s, this
224 // effectively allows us to treat the version numbers as 'decimals'. This is
225 // a little strange but this method seems to do the right thing for all
226 // different vendor's driver strings. i.e. .98 will become 9800, which is
227 // larger than .978 which would become 9780.
228 void PadDriverDecimal(char *aString)
230 for (int i = 0; i < 4; i++)
232 if (!aString[i])
234 for (int c = i; c < 4; c++)
236 aString[c] = '0';
238 break;
241 aString[4] = 0;
244 // All destination string storage needs to have at least 5 bytes available.
245 bool SplitDriverVersion(const char *aSource, char *aAStr, char *aBStr, char *aCStr, char *aDStr)
247 // sscanf doesn't do what we want here to we parse this manually.
248 int len = strlen(aSource);
249 char *dest[4] = { aAStr, aBStr, aCStr, aDStr };
250 unsigned destIdx = 0;
251 unsigned destPos = 0;
253 for (int i = 0; i < len; i++)
255 if (destIdx >= ArrayLength(dest))
257 // Invalid format found. Ensure we don't access dest beyond bounds.
258 return false;
261 if (aSource[i] == '.')
263 dest[destIdx++][destPos] = 0;
264 destPos = 0;
265 continue;
268 if (destPos > 3)
270 // Ignore more than 4 chars. Ensure we never access dest[destIdx]
271 // beyond its bounds.
272 continue;
275 dest[destIdx][destPos++] = aSource[i];
278 // Add last terminator.
279 dest[destIdx][destPos] = 0;
281 if (destIdx != ArrayLength(dest) - 1)
283 return false;
285 return true;
288 /* Other interesting places for info:
289 * IDXGIAdapter::GetDesc()
290 * IDirectDraw7::GetAvailableVidMem()
291 * e->GetAvailableTextureMem()
292 * */
294 template<typename T> void appendIntegerWithPadding(OUString& rString, T value, sal_uInt32 nChars)
296 rString += "0x";
297 OUString aValue = OUString::number(value, 16);
298 sal_Int32 nLength = aValue.getLength();
299 sal_uInt32 nPadLength = nChars - nLength;
300 assert(nPadLength >= 0);
301 OUStringBuffer aBuffer;
302 for (sal_uInt32 i = 0; i < nPadLength; ++i)
304 aBuffer.append("0");
306 rString += aBuffer.makeStringAndClear() + aValue;
309 #define DEVICE_KEY_PREFIX L"\\Registry\\Machine\\"
312 namespace wgl {
314 bool ParseDriverVersion(const OUString& aVersion, uint64_t& rNumericVersion)
316 rNumericVersion = 0;
318 #if defined(_WIN32)
319 int a, b, c, d;
320 char aStr[8], bStr[8], cStr[8], dStr[8];
321 /* honestly, why do I even bother */
322 OString aOVersion = OUStringToOString(aVersion, RTL_TEXTENCODING_UTF8);
323 if (!SplitDriverVersion(aOVersion.getStr(), aStr, bStr, cStr, dStr))
324 return false;
326 PadDriverDecimal(bStr);
327 PadDriverDecimal(cStr);
328 PadDriverDecimal(dStr);
330 a = atoi(aStr);
331 b = atoi(bStr);
332 c = atoi(cStr);
333 d = atoi(dStr);
335 if (a < 0 || a > 0xffff) return false;
336 if (b < 0 || b > 0xffff) return false;
337 if (c < 0 || c > 0xffff) return false;
338 if (d < 0 || d > 0xffff) return false;
340 rNumericVersion = GFX_DRIVER_VERSION(a, b, c, d);
341 return true;
342 #else
343 return false;
344 #endif
347 uint64_t DriverInfo::allDriverVersions = ~(uint64_t(0));
349 DriverInfo::DriverInfo()
350 : meOperatingSystem(wgl::DRIVER_OS_UNKNOWN),
351 mnOperatingSystemVersion(0),
352 maAdapterVendor(WinOpenGLDeviceInfo::GetDeviceVendor(VendorAll)),
353 mbWhitelisted(false),
354 meComparisonOp(DRIVER_COMPARISON_IGNORED),
355 mnDriverVersion(0),
356 mnDriverVersionMax(0)
359 DriverInfo::DriverInfo(OperatingSystem os, const OUString& vendor,
360 VersionComparisonOp op,
361 uint64_t driverVersion,
362 bool bWhitelisted,
363 const char *suggestedVersion /* = nullptr */)
364 : meOperatingSystem(os),
365 mnOperatingSystemVersion(0),
366 maAdapterVendor(vendor),
367 mbWhitelisted(bWhitelisted),
368 meComparisonOp(op),
369 mnDriverVersion(driverVersion),
370 mnDriverVersionMax(0)
372 if (suggestedVersion)
373 maSuggestedVersion = OStringToOUString(OString(suggestedVersion), RTL_TEXTENCODING_UTF8);
376 DriverInfo::~DriverInfo()
382 WinOpenGLDeviceInfo::WinOpenGLDeviceInfo():
383 mbHasDualGPU(false),
384 mbRDP(false)
386 GetData();
387 FillBlacklist();
390 WinOpenGLDeviceInfo::~WinOpenGLDeviceInfo()
394 namespace {
396 struct compareIgnoreAsciiCase
398 explicit compareIgnoreAsciiCase(const OUString& rString)
399 : maString(rString)
403 bool operator()(const OUString& rCompare)
405 return maString.equalsIgnoreAsciiCase(rCompare);
408 private:
409 OUString maString;
414 bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList(std::vector<wgl::DriverInfo>& aDeviceInfos,
415 OUString const & sDriverVersion, OUString const & sAdapterVendorID,
416 OUString const & sAdapterDeviceID, uint32_t nWindowsVersion)
418 uint64_t driverVersion;
419 wgl::ParseDriverVersion(sDriverVersion, driverVersion);
421 wgl::OperatingSystem eOS = WindowsVersionToOperatingSystem(nWindowsVersion);
422 bool match = false;
423 for (std::vector<wgl::DriverInfo>::size_type i = 0; i < aDeviceInfos.size(); i++)
425 if (aDeviceInfos[i].meOperatingSystem != wgl::DRIVER_OS_ALL &&
426 aDeviceInfos[i].meOperatingSystem != eOS)
428 continue;
431 if (aDeviceInfos[i].mnOperatingSystemVersion && aDeviceInfos[i].mnOperatingSystemVersion != nWindowsVersion)
433 continue;
436 if (!aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(GetDeviceVendor(wgl::VendorAll)) &&
437 !aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(sAdapterVendorID))
439 continue;
442 if (std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(), compareIgnoreAsciiCase("all")) &&
443 std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(), compareIgnoreAsciiCase(sAdapterDeviceID)))
445 continue;
448 switch (aDeviceInfos[i].meComparisonOp)
450 case wgl::DRIVER_LESS_THAN:
451 match = driverVersion < aDeviceInfos[i].mnDriverVersion;
452 break;
453 case wgl::DRIVER_LESS_THAN_OR_EQUAL:
454 match = driverVersion <= aDeviceInfos[i].mnDriverVersion;
455 break;
456 case wgl::DRIVER_GREATER_THAN:
457 match = driverVersion > aDeviceInfos[i].mnDriverVersion;
458 break;
459 case wgl::DRIVER_GREATER_THAN_OR_EQUAL:
460 match = driverVersion >= aDeviceInfos[i].mnDriverVersion;
461 break;
462 case wgl::DRIVER_EQUAL:
463 match = driverVersion == aDeviceInfos[i].mnDriverVersion;
464 break;
465 case wgl::DRIVER_NOT_EQUAL:
466 match = driverVersion != aDeviceInfos[i].mnDriverVersion;
467 break;
468 case wgl::DRIVER_BETWEEN_EXCLUSIVE:
469 match = driverVersion > aDeviceInfos[i].mnDriverVersion && driverVersion < aDeviceInfos[i].mnDriverVersionMax;
470 break;
471 case wgl::DRIVER_BETWEEN_INCLUSIVE:
472 match = driverVersion >= aDeviceInfos[i].mnDriverVersion && driverVersion <= aDeviceInfos[i].mnDriverVersionMax;
473 break;
474 case wgl::DRIVER_BETWEEN_INCLUSIVE_START:
475 match = driverVersion >= aDeviceInfos[i].mnDriverVersion && driverVersion < aDeviceInfos[i].mnDriverVersionMax;
476 break;
477 case wgl::DRIVER_COMPARISON_IGNORED:
478 // We don't have a comparison op, so we match everything.
479 match = true;
480 break;
481 default:
482 SAL_WARN("vcl.opengl", "Bogus op in GfxDriverInfo");
483 break;
486 if (match || aDeviceInfos[i].mnDriverVersion == wgl::DriverInfo::allDriverVersions)
488 // white listed drivers
489 if (aDeviceInfos[i].mbWhitelisted)
491 SAL_WARN("vcl.opengl", "whitelisted driver");
492 return false;
495 match = true;
496 SAL_WARN("vcl.opengl", "use : " << aDeviceInfos[i].maSuggestedVersion);
497 break;
501 SAL_INFO("vcl.opengl", (match ? "BLACKLISTED" : "not blacklisted"));
502 return match;
505 bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList()
507 return FindBlocklistedDeviceInList(maDriverInfo, maDriverVersion, maAdapterVendorID, maAdapterDeviceID, mnWindowsVersion);
510 namespace {
512 OUString getCacheFolder()
514 OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/");
515 rtl::Bootstrap::expandMacros(url);
517 osl::Directory::create(url);
519 return url;
522 void writeToLog(SvStream& rStrm, const char* pKey, const OUString & rVal)
524 rStrm.WriteCharPtr(pKey);
525 rStrm.WriteCharPtr(": ");
526 rStrm.WriteOString(OUStringToOString(rVal, RTL_TEXTENCODING_UTF8));
527 rStrm.WriteChar('\n');
532 bool WinOpenGLDeviceInfo::isDeviceBlocked()
534 CrashReporter::addKeyValue("OpenGLVendor", maAdapterVendorID, CrashReporter::AddItem);
535 CrashReporter::addKeyValue("OpenGLDevice", maAdapterDeviceID, CrashReporter::AddItem);
536 CrashReporter::addKeyValue("OpenGLDriver", maDriverVersion, CrashReporter::Write);
538 SAL_INFO("vcl.opengl", maDriverVersion);
539 SAL_INFO("vcl.opengl", maDriverDate);
540 SAL_INFO("vcl.opengl", maDeviceID);
541 SAL_INFO("vcl.opengl", maAdapterVendorID);
542 SAL_INFO("vcl.opengl", maAdapterDeviceID);
543 SAL_INFO("vcl.opengl", maAdapterSubsysID);
544 SAL_INFO("vcl.opengl", maDeviceKey);
545 SAL_INFO("vcl.opengl", maDeviceString);
547 OUString aCacheFolder = getCacheFolder();
549 OUString aCacheFile(aCacheFolder + "/opengl_device.log");
550 SvFileStream aOpenGLLogFile(aCacheFile, StreamMode::WRITE);
552 writeToLog(aOpenGLLogFile, "DriverVersion", maDriverVersion);
553 writeToLog(aOpenGLLogFile, "DriverDate", maDriverDate);
554 writeToLog(aOpenGLLogFile, "DeviceID", maDeviceID);
555 writeToLog(aOpenGLLogFile, "AdapterVendorID", maAdapterVendorID);
556 writeToLog(aOpenGLLogFile, "AdapterDeviceID", maAdapterDeviceID);
557 writeToLog(aOpenGLLogFile, "AdapterSubsysID", maAdapterSubsysID);
558 writeToLog(aOpenGLLogFile, "DeviceKey", maDeviceKey);
559 writeToLog(aOpenGLLogFile, "DeviceString", maDeviceString);
561 // Check if the device is blocked from the downloaded blocklist. If not, check
562 // the static list after that. This order is used so that we can later escape
563 // out of static blocks (i.e. if we were wrong or something was patched, we
564 // can back out our static block without doing a release).
565 if (mbRDP)
567 SAL_WARN("vcl.opengl", "all OpenGL blocked for RDP sessions");
568 return true;
571 return FindBlocklistedDeviceInList();
574 void WinOpenGLDeviceInfo::GetData()
576 DISPLAY_DEVICEW displayDevice;
577 displayDevice.cb = sizeof(displayDevice);
579 mnWindowsVersion = WindowsOSVersion();
580 int deviceIndex = 0;
582 while (EnumDisplayDevicesW(nullptr, deviceIndex, &displayDevice, 0))
584 if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
586 break;
588 deviceIndex++;
591 // make sure the string is null terminated
592 // (using the term "null" here to mean a zero UTF-16 unit)
593 if (wcsnlen(displayDevice.DeviceKey, ArrayLength(displayDevice.DeviceKey))
594 == ArrayLength(displayDevice.DeviceKey))
596 // we did not find a null
597 SAL_WARN("vcl.opengl", "string not null terminated");
598 return;
601 /* DeviceKey is "reserved" according to MSDN so we'll be careful with it */
602 /* check that DeviceKey begins with DEVICE_KEY_PREFIX */
603 /* some systems have a DeviceKey starting with \REGISTRY\Machine\ so we need to compare case insensitively */
604 if (_wcsnicmp(displayDevice.DeviceKey, DEVICE_KEY_PREFIX, ArrayLength(DEVICE_KEY_PREFIX)-1) != 0)
606 SAL_WARN("vcl.opengl", "incorrect DeviceKey");
607 return;
610 // chop off DEVICE_KEY_PREFIX
611 maDeviceKey = o3tl::toU(displayDevice.DeviceKey) + ArrayLength(DEVICE_KEY_PREFIX)-1;
613 maDeviceID = o3tl::toU(displayDevice.DeviceID);
614 maDeviceString = o3tl::toU(displayDevice.DeviceString);
616 if (maDeviceID.isEmpty() &&
617 (maDeviceString == "RDPDD Chained DD" ||
618 (maDeviceString == "RDPUDD Chained DD")))
620 // we need to block RDP as it does not provide OpenGL 2.1+
621 mbRDP = true;
622 SAL_WARN("vcl.opengl", "RDP => blocked");
623 return;
626 /* create a device information set composed of the current display device */
627 HDEVINFO devinfo = SetupDiGetClassDevsW(nullptr, o3tl::toW(maDeviceID.getStr()), nullptr,
628 DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
630 if (devinfo != INVALID_HANDLE_VALUE)
632 HKEY key;
633 LONG result;
634 WCHAR value[255];
635 DWORD dwcbData;
636 SP_DEVINFO_DATA devinfoData;
637 DWORD memberIndex = 0;
639 devinfoData.cbSize = sizeof(devinfoData);
640 /* enumerate device information elements in the device information set */
641 while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
643 /* get a string that identifies the device's driver key */
644 if (SetupDiGetDeviceRegistryPropertyW(devinfo,
645 &devinfoData,
646 SPDRP_DRIVER,
647 nullptr,
648 reinterpret_cast<PBYTE>(value),
649 sizeof(value),
650 nullptr))
652 OUString driverKey(OUStringLiteral("System\\CurrentControlSet\\Control\\Class\\") + o3tl::toU(value));
653 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, o3tl::toW(driverKey.getStr()), 0, KEY_QUERY_VALUE, &key);
654 if (result == ERROR_SUCCESS)
656 /* we've found the driver we're looking for */
657 dwcbData = sizeof(value);
658 result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
659 reinterpret_cast<LPBYTE>(value), &dwcbData);
660 if (result == ERROR_SUCCESS)
662 maDriverVersion = OUString(o3tl::toU(value));
664 else
666 // If the entry wasn't found, assume the worst (0.0.0.0).
667 maDriverVersion = OUString("0.0.0.0");
669 dwcbData = sizeof(value);
670 result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
671 reinterpret_cast<LPBYTE>(value), &dwcbData);
672 if (result == ERROR_SUCCESS)
674 maDriverDate = o3tl::toU(value);
676 else
678 // Again, assume the worst
679 maDriverDate = OUString("01-01-1970");
681 RegCloseKey(key);
682 break;
687 SetupDiDestroyDeviceInfoList(devinfo);
689 else
691 SAL_WARN("vcl.opengl", "invalid handle value");
694 appendIntegerWithPadding(maAdapterVendorID, ParseIDFromDeviceID(maDeviceID, "VEN_", 4), 4);
695 appendIntegerWithPadding(maAdapterDeviceID, ParseIDFromDeviceID(maDeviceID, "&DEV_", 4), 4);
696 appendIntegerWithPadding(maAdapterSubsysID, ParseIDFromDeviceID(maDeviceID, "&SUBSYS_", 8), 8);
698 // We now check for second display adapter.
700 // Device interface class for display adapters.
701 CLSID GUID_DISPLAY_DEVICE_ARRIVAL;
702 HRESULT hresult = CLSIDFromString(L"{1CA05180-A699-450A-9A0C-DE4FBE3DDD89}",
703 &GUID_DISPLAY_DEVICE_ARRIVAL);
704 if (hresult == NOERROR)
706 devinfo = SetupDiGetClassDevsW(&GUID_DISPLAY_DEVICE_ARRIVAL,
707 nullptr, nullptr,
708 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
710 if (devinfo != INVALID_HANDLE_VALUE)
712 HKEY key;
713 LONG result;
714 WCHAR value[255];
715 DWORD dwcbData;
716 SP_DEVINFO_DATA devinfoData;
717 DWORD memberIndex = 0;
718 devinfoData.cbSize = sizeof(devinfoData);
720 OUString aAdapterDriver2;
721 OUString aDeviceID2;
722 OUString aDriverVersion2;
723 OUString aDriverDate2;
724 uint32_t adapterVendorID2;
725 uint32_t adapterDeviceID2;
727 /* enumerate device information elements in the device information set */
728 while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
730 /* get a string that identifies the device's driver key */
731 if (SetupDiGetDeviceRegistryPropertyW(devinfo,
732 &devinfoData,
733 SPDRP_DRIVER,
734 nullptr,
735 reinterpret_cast<PBYTE>(value),
736 sizeof(value),
737 nullptr))
739 OUString driverKey2(OUStringLiteral("System\\CurrentControlSet\\Control\\Class\\") + o3tl::toU(value));
740 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, o3tl::toW(driverKey2.getStr()), 0, KEY_QUERY_VALUE, &key);
741 if (result == ERROR_SUCCESS)
743 dwcbData = sizeof(value);
744 result = RegQueryValueExW(key, L"MatchingDeviceId", nullptr,
745 nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData);
746 if (result != ERROR_SUCCESS)
748 continue;
750 aDeviceID2 = o3tl::toU(value);
751 OUString aAdapterVendorID2String;
752 OUString aAdapterDeviceID2String;
753 adapterVendorID2 = ParseIDFromDeviceID(aDeviceID2, "VEN_", 4);
754 appendIntegerWithPadding(aAdapterVendorID2String, adapterVendorID2, 4);
755 adapterDeviceID2 = ParseIDFromDeviceID(aDeviceID2, "&DEV_", 4);
756 appendIntegerWithPadding(aAdapterDeviceID2String, adapterDeviceID2, 4);
757 if (maAdapterVendorID == aAdapterVendorID2String &&
758 maAdapterDeviceID == aAdapterDeviceID2String)
760 RegCloseKey(key);
761 continue;
764 // If this device is missing driver information, it is unlikely to
765 // be a real display adapter.
766 if (!GetKeyValue(o3tl::toW(driverKey2.getStr()), L"InstalledDisplayDrivers",
767 aAdapterDriver2, REG_MULTI_SZ))
769 RegCloseKey(key);
770 continue;
772 dwcbData = sizeof(value);
773 result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
774 reinterpret_cast<LPBYTE>(value), &dwcbData);
775 if (result != ERROR_SUCCESS)
777 RegCloseKey(key);
778 continue;
780 aDriverVersion2 = o3tl::toU(value);
781 dwcbData = sizeof(value);
782 result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
783 reinterpret_cast<LPBYTE>(value), &dwcbData);
784 if (result != ERROR_SUCCESS)
786 RegCloseKey(key);
787 continue;
789 aDriverDate2 = o3tl::toU(value);
790 dwcbData = sizeof(value);
791 result = RegQueryValueExW(key, L"Device Description", nullptr,
792 nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData);
793 if (result != ERROR_SUCCESS)
795 dwcbData = sizeof(value);
796 result = RegQueryValueExW(key, L"DriverDesc", nullptr, nullptr,
797 reinterpret_cast<LPBYTE>(value), &dwcbData);
799 RegCloseKey(key);
800 if (result == ERROR_SUCCESS)
802 mbHasDualGPU = true;
803 maDeviceString2 = o3tl::toU(value);
804 maDeviceID2 = aDeviceID2;
805 maDeviceKey2 = driverKey2;
806 maDriverVersion2 = aDriverVersion2;
807 maDriverDate2 = aDriverDate2;
808 appendIntegerWithPadding(maAdapterVendorID2, adapterVendorID2, 4);
809 appendIntegerWithPadding(maAdapterDeviceID2, adapterDeviceID2, 4);
810 appendIntegerWithPadding(maAdapterSubsysID2, ParseIDFromDeviceID(maDeviceID2, "&SUBSYS_", 8), 8);
811 break;
817 SetupDiDestroyDeviceInfoList(devinfo);
822 OUString WinOpenGLDeviceInfo::GetDeviceVendor(wgl::DeviceVendor id)
824 assert(id >= 0 && id < wgl::DeviceVendorMax);
826 if (mpDeviceVendors[id])
827 return *mpDeviceVendors[id];
829 mpDeviceVendors[id] = new OUString();
831 switch (id)
833 case wgl::VendorAll:
834 *mpDeviceVendors[id] = "";
835 break;
836 case wgl::VendorIntel:
837 *mpDeviceVendors[id] = "0x8086";
838 break;
839 case wgl::VendorNVIDIA:
840 *mpDeviceVendors[id] = "0x10de";
841 break;
842 case wgl::VendorAMD:
843 *mpDeviceVendors[id] = "0x1022";
844 break;
845 case wgl::VendorATI:
846 *mpDeviceVendors[id] = "0x1002";
847 break;
848 case wgl::VendorMicrosoft:
849 *mpDeviceVendors[id] = "0x1414";
850 break;
851 case wgl::DeviceVendorMax: // Suppress a warning.
852 break;
855 return *mpDeviceVendors[id];
858 namespace {
861 OUString getBlacklistFile()
863 OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER);
864 rtl::Bootstrap::expandMacros(url);
866 return url + "/opengl/opengl_blacklist_windows.xml";
872 void WinOpenGLDeviceInfo::FillBlacklist()
874 OUString aURL = getBlacklistFile();
875 WinBlocklistParser aParser(aURL, maDriverInfo);
876 try {
877 aParser.parse();
879 catch (...)
881 SAL_WARN("vcl.opengl", "error parsing blacklist");
882 maDriverInfo.clear();
887 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */