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