1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
10 #include <opengl/win/WinDeviceInfo.hxx>
11 
12 #include <opengl/win/blocklist_parser.hxx>
13 #include <config_folders.h>
14 
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>
24 
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>
31 
32 #include <desktop/crashreport.hxx>
33 
34 OUString* WinOpenGLDeviceInfo::mpDeviceVendors[wgl::DeviceVendorMax];
35 std::vector<wgl::DriverInfo> WinOpenGLDeviceInfo::maDriverInfo;
36 
37 namespace {
38 
39 /*
40  * Compute the length of an array with constant length.  (Use of this method
41  * with a non-array pointer will not compile.)
42  *
43  * Beware of the implicit trailing '\0' when using this with string constants.
44 */
45 template<typename T, size_t N>
ArrayLength(T (&)[N])46 size_t ArrayLength(T (&)[N])
47 {
48     return N;
49 }
50 
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))
53 
GetKeyValue(const WCHAR * keyLocation,const WCHAR * keyName,OUString & destString,int type)54 bool GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName, OUString& destString, int type)
55 {
56     HKEY key;
57     DWORD dwcbData;
58     DWORD dValue;
59     DWORD resultType;
60     LONG result;
61     bool retval = true;
62 
63     result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyLocation, 0, KEY_QUERY_VALUE, &key);
64     if (result != ERROR_SUCCESS)
65     {
66         return false;
67     }
68 
69     switch (type)
70     {
71         case REG_DWORD:
72             {
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)
78                 {
79                     dValue = dValue / 1024 / 1024;
80                     destString += OUString::number(int32_t(dValue));
81                 }
82                 else
83                 {
84                     retval = false;
85                 }
86                 break;
87             }
88         case REG_MULTI_SZ:
89             {
90                 // A chain of null-separated strings; we convert the nulls to spaces
91                 WCHAR wCharValue[1024];
92                 dwcbData = sizeof(wCharValue);
93 
94                 result = RegQueryValueExW(key, keyName, nullptr, &resultType,
95                         reinterpret_cast<LPBYTE>(wCharValue), &dwcbData);
96                 if (result == ERROR_SUCCESS && resultType == REG_MULTI_SZ)
97                 {
98                     // This bit here could probably be cleaner.
99                     bool isValid = false;
100 
101                     DWORD strLen = dwcbData/sizeof(wCharValue[0]);
102                     for (DWORD i = 0; i < strLen; i++)
103                     {
104                         if (wCharValue[i] == '\0')
105                         {
106                             if (i < strLen - 1 && wCharValue[i + 1] == '\0')
107                             {
108                                 isValid = true;
109                                 break;
110                             }
111                             else
112                             {
113                                 wCharValue[i] = ' ';
114                             }
115                         }
116                     }
117 
118                     // ensure wCharValue is null terminated
119                     wCharValue[strLen-1] = '\0';
120 
121                     if (isValid)
122                         destString = OUString(o3tl::toU(wCharValue));
123 
124                 }
125                 else
126                 {
127                     retval = false;
128                 }
129 
130                 break;
131             }
132     }
133     RegCloseKey(key);
134 
135     return retval;
136 }
137 
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
ParseIDFromDeviceID(const OUString & key,const char * prefix,int length)140 uint32_t ParseIDFromDeviceID(const OUString &key, const char *prefix, int length)
141 {
142     OUString id = key.toAsciiUpperCase();
143     OUString aPrefix = OUString::fromUtf8(prefix);
144     int32_t start = id.indexOf(aPrefix);
145     if (start != -1)
146     {
147         id = id.copy(start + aPrefix.getLength(), length);
148     }
149     return id.toUInt32(16);
150 }
151 
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
160 };
161 
162 
WindowsVersionToOperatingSystem(int32_t aWindowsVersion)163 wgl::OperatingSystem WindowsVersionToOperatingSystem(int32_t aWindowsVersion)
164 {
165     switch(aWindowsVersion)
166     {
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;
178     };
179 }
180 
181 
WindowsOSVersion()182 int32_t WindowsOSVersion()
183 {
184     static int32_t winVersion = [&]()
185     {
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)
194         {
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))
199             {
200                 dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
201                 if (dwCount != 0)
202                 {
203                     std::unique_ptr<char[]> ver(new char[dwCount]);
204                     if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
205                     {
206                         void* pBlock = nullptr;
207                         UINT dwBlockSz = 0;
208                         if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
209                         {
210                             VS_FIXEDFILEINFO *vinfo = static_cast<VS_FIXEDFILEINFO *>(pBlock);
211                             return int32_t(vinfo->dwProductVersionMS);
212                         }
213                     }
214                 }
215             }
216         }
217         return int32_t(kWindowsUnknown);
218     }();
219 
220     return winVersion;
221 }
222 
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.
PadDriverDecimal(char * aString)228 void PadDriverDecimal(char *aString)
229 {
230     for (int i = 0; i < 4; i++)
231     {
232         if (!aString[i])
233         {
234             for (int c = i; c < 4; c++)
235             {
236                 aString[c] = '0';
237             }
238             break;
239         }
240     }
241     aString[4] = 0;
242 }
243 
244 // All destination string storage needs to have at least 5 bytes available.
SplitDriverVersion(const char * aSource,char * aAStr,char * aBStr,char * aCStr,char * aDStr)245 bool SplitDriverVersion(const char *aSource, char *aAStr, char *aBStr, char *aCStr, char *aDStr)
246 {
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;
252 
253     for (int i = 0; i < len; i++)
254     {
255         if (destIdx >= ArrayLength(dest))
256         {
257             // Invalid format found. Ensure we don't access dest beyond bounds.
258             return false;
259         }
260 
261         if (aSource[i] == '.')
262         {
263             dest[destIdx++][destPos] = 0;
264             destPos = 0;
265             continue;
266         }
267 
268         if (destPos > 3)
269         {
270             // Ignore more than 4 chars. Ensure we never access dest[destIdx]
271             // beyond its bounds.
272             continue;
273         }
274 
275         dest[destIdx][destPos++] = aSource[i];
276     }
277 
278     // Add last terminator.
279     dest[destIdx][destPos] = 0;
280 
281     if (destIdx != ArrayLength(dest) - 1)
282     {
283         return false;
284     }
285     return true;
286 }
287 
288 /* Other interesting places for info:
289  *   IDXGIAdapter::GetDesc()
290  *   IDirectDraw7::GetAvailableVidMem()
291  *   e->GetAvailableTextureMem()
292  * */
293 
appendIntegerWithPadding(OUString & rString,T value,sal_uInt32 nChars)294 template<typename T> void appendIntegerWithPadding(OUString& rString, T value, sal_uInt32 nChars)
295 {
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)
303     {
304         aBuffer.append("0");
305     }
306     rString += aBuffer.makeStringAndClear() + aValue;
307 }
308 
309 #define DEVICE_KEY_PREFIX L"\\Registry\\Machine\\"
310 }
311 
312 namespace wgl {
313 
ParseDriverVersion(const OUString & aVersion,uint64_t & rNumericVersion)314 bool ParseDriverVersion(const OUString& aVersion, uint64_t& rNumericVersion)
315 {
316     rNumericVersion = 0;
317 
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;
325 
326     PadDriverDecimal(bStr);
327     PadDriverDecimal(cStr);
328     PadDriverDecimal(dStr);
329 
330     a = atoi(aStr);
331     b = atoi(bStr);
332     c = atoi(cStr);
333     d = atoi(dStr);
334 
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;
339 
340     rNumericVersion = GFX_DRIVER_VERSION(a, b, c, d);
341     return true;
342 #else
343     return false;
344 #endif
345 }
346 
347 uint64_t DriverInfo::allDriverVersions = ~(uint64_t(0));
348 
DriverInfo()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)
357 {}
358 
DriverInfo(OperatingSystem os,const OUString & vendor,VersionComparisonOp op,uint64_t driverVersion,bool bWhitelisted,const char * suggestedVersion)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)
371 {
372     if (suggestedVersion)
373         maSuggestedVersion = OStringToOUString(OString(suggestedVersion), RTL_TEXTENCODING_UTF8);
374 }
375 
~DriverInfo()376 DriverInfo::~DriverInfo()
377 {
378 }
379 
380 }
381 
WinOpenGLDeviceInfo()382 WinOpenGLDeviceInfo::WinOpenGLDeviceInfo():
383     mbHasDualGPU(false),
384     mbRDP(false)
385 {
386     GetData();
387     FillBlacklist();
388 }
389 
~WinOpenGLDeviceInfo()390 WinOpenGLDeviceInfo::~WinOpenGLDeviceInfo()
391 {
392 }
393 
394 namespace {
395 
396 struct compareIgnoreAsciiCase
397 {
compareIgnoreAsciiCase__anon0ed722d50411::compareIgnoreAsciiCase398     explicit compareIgnoreAsciiCase(const OUString& rString)
399         : maString(rString)
400     {
401     }
402 
operator ()__anon0ed722d50411::compareIgnoreAsciiCase403     bool operator()(const OUString& rCompare)
404     {
405         return maString.equalsIgnoreAsciiCase(rCompare);
406     }
407 
408 private:
409     OUString maString;
410 };
411 
412 }
413 
FindBlocklistedDeviceInList(std::vector<wgl::DriverInfo> & aDeviceInfos,OUString const & sDriverVersion,OUString const & sAdapterVendorID,OUString const & sAdapterDeviceID,uint32_t nWindowsVersion)414 bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList(std::vector<wgl::DriverInfo>& aDeviceInfos,
415                                                       OUString const & sDriverVersion, OUString const & sAdapterVendorID,
416                                                       OUString const & sAdapterDeviceID, uint32_t nWindowsVersion)
417 {
418     uint64_t driverVersion;
419     wgl::ParseDriverVersion(sDriverVersion, driverVersion);
420 
421     wgl::OperatingSystem eOS = WindowsVersionToOperatingSystem(nWindowsVersion);
422     bool match = false;
423     for (std::vector<wgl::DriverInfo>::size_type i = 0; i < aDeviceInfos.size(); i++)
424     {
425         if (aDeviceInfos[i].meOperatingSystem != wgl::DRIVER_OS_ALL &&
426                 aDeviceInfos[i].meOperatingSystem != eOS)
427         {
428             continue;
429         }
430 
431         if (aDeviceInfos[i].mnOperatingSystemVersion && aDeviceInfos[i].mnOperatingSystemVersion != nWindowsVersion)
432         {
433             continue;
434         }
435 
436         if (!aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(GetDeviceVendor(wgl::VendorAll)) &&
437                 !aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(sAdapterVendorID))
438         {
439             continue;
440         }
441 
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)))
444         {
445             continue;
446         }
447 
448         switch (aDeviceInfos[i].meComparisonOp)
449         {
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;
484         }
485 
486         if (match || aDeviceInfos[i].mnDriverVersion == wgl::DriverInfo::allDriverVersions)
487         {
488             // white listed drivers
489             if (aDeviceInfos[i].mbWhitelisted)
490             {
491                 SAL_WARN("vcl.opengl", "whitelisted driver");
492                 return false;
493             }
494 
495             match = true;
496             SAL_WARN("vcl.opengl", "use : " << aDeviceInfos[i].maSuggestedVersion);
497             break;
498         }
499     }
500 
501     SAL_INFO("vcl.opengl", (match ? "BLACKLISTED" : "not blacklisted"));
502     return match;
503 }
504 
FindBlocklistedDeviceInList()505 bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList()
506 {
507     return FindBlocklistedDeviceInList(maDriverInfo, maDriverVersion, maAdapterVendorID, maAdapterDeviceID, mnWindowsVersion);
508 }
509 
510 namespace {
511 
getCacheFolder()512 OUString getCacheFolder()
513 {
514     OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/");
515     rtl::Bootstrap::expandMacros(url);
516 
517     osl::Directory::create(url);
518 
519     return url;
520 }
521 
writeToLog(SvStream & rStrm,const char * pKey,const OUString & rVal)522 void writeToLog(SvStream& rStrm, const char* pKey, const OUString & rVal)
523 {
524     rStrm.WriteCharPtr(pKey);
525     rStrm.WriteCharPtr(": ");
526     rStrm.WriteOString(OUStringToOString(rVal, RTL_TEXTENCODING_UTF8));
527     rStrm.WriteChar('\n');
528 }
529 
530 }
531 
isDeviceBlocked()532 bool WinOpenGLDeviceInfo::isDeviceBlocked()
533 {
534     CrashReporter::addKeyValue("OpenGLVendor", maAdapterVendorID, CrashReporter::AddItem);
535     CrashReporter::addKeyValue("OpenGLDevice", maAdapterDeviceID, CrashReporter::AddItem);
536     CrashReporter::addKeyValue("OpenGLDriver", maDriverVersion, CrashReporter::Write);
537 
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);
546 
547     OUString aCacheFolder = getCacheFolder();
548 
549     OUString aCacheFile(aCacheFolder + "/opengl_device.log");
550     SvFileStream aOpenGLLogFile(aCacheFile, StreamMode::WRITE);
551 
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);
560 
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)
566     {
567         SAL_WARN("vcl.opengl", "all OpenGL blocked for RDP sessions");
568         return true;
569     }
570 
571     return FindBlocklistedDeviceInList();
572 }
573 
GetData()574 void WinOpenGLDeviceInfo::GetData()
575 {
576     DISPLAY_DEVICEW displayDevice;
577     displayDevice.cb = sizeof(displayDevice);
578 
579     mnWindowsVersion = WindowsOSVersion();
580     int deviceIndex = 0;
581 
582     while (EnumDisplayDevicesW(nullptr, deviceIndex, &displayDevice, 0))
583     {
584         if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
585         {
586             break;
587         }
588         deviceIndex++;
589     }
590 
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))
595     {
596         // we did not find a null
597         SAL_WARN("vcl.opengl", "string not null terminated");
598         return;
599     }
600 
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)
605     {
606         SAL_WARN("vcl.opengl", "incorrect DeviceKey");
607         return;
608     }
609 
610     // chop off DEVICE_KEY_PREFIX
611     maDeviceKey = o3tl::toU(displayDevice.DeviceKey) + ArrayLength(DEVICE_KEY_PREFIX)-1;
612 
613     maDeviceID = o3tl::toU(displayDevice.DeviceID);
614     maDeviceString = o3tl::toU(displayDevice.DeviceString);
615 
616     if (maDeviceID.isEmpty() &&
617         (maDeviceString == "RDPDD Chained DD" ||
618          (maDeviceString == "RDPUDD Chained DD")))
619     {
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;
624     }
625 
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);
629 
630     if (devinfo != INVALID_HANDLE_VALUE)
631     {
632         HKEY key;
633         LONG result;
634         WCHAR value[255];
635         DWORD dwcbData;
636         SP_DEVINFO_DATA devinfoData;
637         DWORD memberIndex = 0;
638 
639         devinfoData.cbSize = sizeof(devinfoData);
640         /* enumerate device information elements in the device information set */
641         while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
642         {
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))
651             {
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)
655                 {
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)
661                     {
662                         maDriverVersion = OUString(o3tl::toU(value));
663                     }
664                     else
665                     {
666                         // If the entry wasn't found, assume the worst (0.0.0.0).
667                         maDriverVersion = OUString("0.0.0.0");
668                     }
669                     dwcbData = sizeof(value);
670                     result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
671                             reinterpret_cast<LPBYTE>(value), &dwcbData);
672                     if (result == ERROR_SUCCESS)
673                     {
674                         maDriverDate = o3tl::toU(value);
675                     }
676                     else
677                     {
678                         // Again, assume the worst
679                         maDriverDate = OUString("01-01-1970");
680                     }
681                     RegCloseKey(key);
682                     break;
683                 }
684             }
685         }
686 
687         SetupDiDestroyDeviceInfoList(devinfo);
688     }
689     else
690     {
691         SAL_WARN("vcl.opengl", "invalid handle value");
692     }
693 
694     appendIntegerWithPadding(maAdapterVendorID, ParseIDFromDeviceID(maDeviceID, "VEN_", 4), 4);
695     appendIntegerWithPadding(maAdapterDeviceID, ParseIDFromDeviceID(maDeviceID, "&DEV_", 4), 4);
696     appendIntegerWithPadding(maAdapterSubsysID, ParseIDFromDeviceID(maDeviceID, "&SUBSYS_", 8), 8);
697 
698     // We now check for second display adapter.
699 
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)
705     {
706         devinfo = SetupDiGetClassDevsW(&GUID_DISPLAY_DEVICE_ARRIVAL,
707                 nullptr, nullptr,
708                 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
709 
710         if (devinfo != INVALID_HANDLE_VALUE)
711         {
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);
719 
720             OUString aAdapterDriver2;
721             OUString aDeviceID2;
722             OUString aDriverVersion2;
723             OUString aDriverDate2;
724             uint32_t adapterVendorID2;
725             uint32_t adapterDeviceID2;
726 
727             /* enumerate device information elements in the device information set */
728             while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
729             {
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))
738                 {
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)
742                     {
743                         dwcbData = sizeof(value);
744                         result = RegQueryValueExW(key, L"MatchingDeviceId", nullptr,
745                                 nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData);
746                         if (result != ERROR_SUCCESS)
747                         {
748                             continue;
749                         }
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)
759                         {
760                             RegCloseKey(key);
761                             continue;
762                         }
763 
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))
768                         {
769                             RegCloseKey(key);
770                             continue;
771                         }
772                         dwcbData = sizeof(value);
773                         result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
774                                 reinterpret_cast<LPBYTE>(value), &dwcbData);
775                         if (result != ERROR_SUCCESS)
776                         {
777                             RegCloseKey(key);
778                             continue;
779                         }
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)
785                         {
786                             RegCloseKey(key);
787                             continue;
788                         }
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)
794                         {
795                             dwcbData = sizeof(value);
796                             result = RegQueryValueExW(key, L"DriverDesc", nullptr, nullptr,
797                                     reinterpret_cast<LPBYTE>(value), &dwcbData);
798                         }
799                         RegCloseKey(key);
800                         if (result == ERROR_SUCCESS)
801                         {
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;
812                         }
813                     }
814                 }
815             }
816 
817             SetupDiDestroyDeviceInfoList(devinfo);
818         }
819     }
820 }
821 
GetDeviceVendor(wgl::DeviceVendor id)822 OUString WinOpenGLDeviceInfo::GetDeviceVendor(wgl::DeviceVendor id)
823 {
824     assert(id >= 0 && id < wgl::DeviceVendorMax);
825 
826     if (mpDeviceVendors[id])
827         return *mpDeviceVendors[id];
828 
829     mpDeviceVendors[id] = new OUString();
830 
831     switch (id)
832     {
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;
853     }
854 
855     return *mpDeviceVendors[id];
856 }
857 
858 namespace {
859 
860 
getBlacklistFile()861 OUString getBlacklistFile()
862 {
863     OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER);
864     rtl::Bootstrap::expandMacros(url);
865 
866     return url + "/opengl/opengl_blacklist_windows.xml";
867 }
868 
869 
870 }
871 
FillBlacklist()872 void WinOpenGLDeviceInfo::FillBlacklist()
873 {
874     OUString aURL = getBlacklistFile();
875     WinBlocklistParser aParser(aURL, maDriverInfo);
876     try {
877         aParser.parse();
878     }
879     catch (...)
880     {
881         SAL_WARN("vcl.opengl", "error parsing blacklist");
882         maDriverInfo.clear();
883     }
884 }
885 
886 
887 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
888