Apparently in some remote desktop situations the device string uses "RDPDD" and not "RDPUDD". No idea what the semantic difference is. Change-Id: I85532b90d759d02fffb73d0f3d22166aefd4edab
876 lines
28 KiB
C++
876 lines
28 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "opengl/win/WinDeviceInfo.hxx"
|
|
|
|
#include "opengl/win/blocklist_parser.hxx"
|
|
#include <config_folders.h>
|
|
|
|
#include <windows.h>
|
|
#include <setupapi.h>
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
|
|
#include <osl/file.hxx>
|
|
#include <rtl/bootstrap.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <tools/stream.hxx>
|
|
|
|
#include <desktop/crashreport.hxx>
|
|
|
|
OUString* WinOpenGLDeviceInfo::mpDeviceVendors[wgl::DeviceVendorMax];
|
|
std::vector<wgl::DriverInfo> WinOpenGLDeviceInfo::maDriverInfo;
|
|
|
|
namespace {
|
|
|
|
/*
|
|
* Compute the length of an array with constant length. (Use of this method
|
|
* with a non-array pointer will not compile.)
|
|
*
|
|
* Beware of the implicit trailing '\0' when using this with string constants.
|
|
*/
|
|
template<typename T, size_t N>
|
|
size_t ArrayLength(T (&aArr)[N])
|
|
{
|
|
(void) aArr;
|
|
return N;
|
|
}
|
|
|
|
#define GFX_DRIVER_VERSION(a,b,c,d) \
|
|
((uint64_t(a)<<48) | (uint64_t(b)<<32) | (uint64_t(c)<<16) | uint64_t(d))
|
|
|
|
bool GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName, OUString& destString, int type)
|
|
{
|
|
HKEY key;
|
|
DWORD dwcbData;
|
|
DWORD dValue;
|
|
DWORD resultType;
|
|
LONG result;
|
|
bool retval = true;
|
|
|
|
result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyLocation, 0, KEY_QUERY_VALUE, &key);
|
|
if (result != ERROR_SUCCESS)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case REG_DWORD:
|
|
{
|
|
// We only use this for vram size
|
|
dwcbData = sizeof(dValue);
|
|
result = RegQueryValueExW(key, keyName, nullptr, &resultType,
|
|
(LPBYTE)&dValue, &dwcbData);
|
|
if (result == ERROR_SUCCESS && resultType == REG_DWORD)
|
|
{
|
|
dValue = dValue / 1024 / 1024;
|
|
destString += OUString::number(int32_t(dValue));
|
|
}
|
|
else
|
|
{
|
|
retval = false;
|
|
}
|
|
break;
|
|
}
|
|
case REG_MULTI_SZ:
|
|
{
|
|
// A chain of null-separated strings; we convert the nulls to spaces
|
|
WCHAR wCharValue[1024];
|
|
dwcbData = sizeof(wCharValue);
|
|
|
|
result = RegQueryValueExW(key, keyName, nullptr, &resultType,
|
|
(LPBYTE)wCharValue, &dwcbData);
|
|
if (result == ERROR_SUCCESS && resultType == REG_MULTI_SZ)
|
|
{
|
|
// This bit here could probably be cleaner.
|
|
bool isValid = false;
|
|
|
|
DWORD strLen = dwcbData/sizeof(wCharValue[0]);
|
|
for (DWORD i = 0; i < strLen; i++)
|
|
{
|
|
if (wCharValue[i] == '\0')
|
|
{
|
|
if (i < strLen - 1 && wCharValue[i + 1] == '\0')
|
|
{
|
|
isValid = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
wCharValue[i] = ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
// ensure wCharValue is null terminated
|
|
wCharValue[strLen-1] = '\0';
|
|
|
|
if (isValid)
|
|
destString = OUString(wCharValue);
|
|
|
|
}
|
|
else
|
|
{
|
|
retval = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
RegCloseKey(key);
|
|
|
|
return retval;
|
|
}
|
|
|
|
// The device ID is a string like PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD
|
|
// this function is used to extract the id's out of it
|
|
uint32_t ParseIDFromDeviceID(const OUString &key, const char *prefix, int length)
|
|
{
|
|
OUString id = key.toAsciiUpperCase();
|
|
OUString aPrefix = OUString::fromUtf8(prefix);
|
|
int32_t start = id.indexOf(aPrefix);
|
|
if (start != -1)
|
|
{
|
|
id = id.copy(start + aPrefix.getLength(), length);
|
|
}
|
|
return id.toUInt32(16);
|
|
}
|
|
|
|
// OS version in 16.16 major/minor form
|
|
// based on http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
|
|
enum {
|
|
kWindowsUnknown = 0,
|
|
kWindowsXP = 0x00050001,
|
|
kWindowsServer2003 = 0x00050002,
|
|
kWindowsVista = 0x00060000,
|
|
kWindows7 = 0x00060001,
|
|
kWindows8 = 0x00060002,
|
|
kWindows8_1 = 0x00060003,
|
|
kWindows10 = 0x000A0000 // Major 10 Minor 0
|
|
};
|
|
|
|
|
|
wgl::OperatingSystem WindowsVersionToOperatingSystem(int32_t aWindowsVersion)
|
|
{
|
|
switch(aWindowsVersion)
|
|
{
|
|
case kWindowsXP:
|
|
return wgl::DRIVER_OS_WINDOWS_XP;
|
|
case kWindowsServer2003:
|
|
return wgl::DRIVER_OS_WINDOWS_SERVER_2003;
|
|
case kWindowsVista:
|
|
return wgl::DRIVER_OS_WINDOWS_VISTA;
|
|
case kWindows7:
|
|
return wgl::DRIVER_OS_WINDOWS_7;
|
|
case kWindows8:
|
|
return wgl::DRIVER_OS_WINDOWS_8;
|
|
case kWindows8_1:
|
|
return wgl::DRIVER_OS_WINDOWS_8_1;
|
|
case kWindows10:
|
|
return wgl::DRIVER_OS_WINDOWS_10;
|
|
case kWindowsUnknown:
|
|
default:
|
|
return wgl::DRIVER_OS_UNKNOWN;
|
|
};
|
|
}
|
|
|
|
|
|
int32_t WindowsOSVersion()
|
|
{
|
|
static int32_t winVersion = kWindowsUnknown;
|
|
|
|
OSVERSIONINFO vinfo;
|
|
|
|
if (winVersion == kWindowsUnknown)
|
|
{
|
|
vinfo.dwOSVersionInfoSize = sizeof (vinfo);
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4996)
|
|
SAL_WNODEPRECATED_DECLARATIONS_PUSH
|
|
if (!GetVersionEx(&vinfo))
|
|
SAL_WNODEPRECATED_DECLARATIONS_POP
|
|
{
|
|
#pragma warning(pop)
|
|
winVersion = kWindowsUnknown;
|
|
}
|
|
else
|
|
{
|
|
winVersion = int32_t(vinfo.dwMajorVersion << 16) + vinfo.dwMinorVersion;
|
|
}
|
|
}
|
|
|
|
return winVersion;
|
|
}
|
|
|
|
// This allows us to pad driver version 'substrings' with 0s, this
|
|
// effectively allows us to treat the version numbers as 'decimals'. This is
|
|
// a little strange but this method seems to do the right thing for all
|
|
// different vendor's driver strings. i.e. .98 will become 9800, which is
|
|
// larger than .978 which would become 9780.
|
|
void PadDriverDecimal(char *aString)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (!aString[i])
|
|
{
|
|
for (int c = i; c < 4; c++)
|
|
{
|
|
aString[c] = '0';
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
aString[4] = 0;
|
|
}
|
|
|
|
// All destination string storage needs to have at least 5 bytes available.
|
|
bool SplitDriverVersion(const char *aSource, char *aAStr, char *aBStr, char *aCStr, char *aDStr)
|
|
{
|
|
// sscanf doesn't do what we want here to we parse this manually.
|
|
int len = strlen(aSource);
|
|
char *dest[4] = { aAStr, aBStr, aCStr, aDStr };
|
|
unsigned destIdx = 0;
|
|
unsigned destPos = 0;
|
|
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
if (destIdx >= ArrayLength(dest))
|
|
{
|
|
// Invalid format found. Ensure we don't access dest beyond bounds.
|
|
return false;
|
|
}
|
|
|
|
if (aSource[i] == '.')
|
|
{
|
|
dest[destIdx++][destPos] = 0;
|
|
destPos = 0;
|
|
continue;
|
|
}
|
|
|
|
if (destPos > 3)
|
|
{
|
|
// Ignore more than 4 chars. Ensure we never access dest[destIdx]
|
|
// beyond its bounds.
|
|
continue;
|
|
}
|
|
|
|
dest[destIdx][destPos++] = aSource[i];
|
|
}
|
|
|
|
// Add last terminator.
|
|
dest[destIdx][destPos] = 0;
|
|
|
|
if (destIdx != ArrayLength(dest) - 1)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Other interesting places for info:
|
|
* IDXGIAdapter::GetDesc()
|
|
* IDirectDraw7::GetAvailableVidMem()
|
|
* e->GetAvailableTextureMem()
|
|
* */
|
|
|
|
template<typename T> void appendIntegerWithPadding(OUString& rString, T value, sal_uInt32 nChars)
|
|
{
|
|
rString += "0x";
|
|
OUString aValue = OUString::number(value, 16);
|
|
sal_Int32 nLength = aValue.getLength();
|
|
sal_uInt32 nPadLength = nChars - nLength;
|
|
assert(nPadLength >= 0);
|
|
OUStringBuffer aBuffer;
|
|
for (sal_uInt32 i = 0; i < nPadLength; ++i)
|
|
{
|
|
aBuffer.append("0");
|
|
}
|
|
rString += aBuffer.makeStringAndClear() + aValue;
|
|
}
|
|
|
|
#define DEVICE_KEY_PREFIX L"\\Registry\\Machine\\"
|
|
}
|
|
|
|
namespace wgl {
|
|
|
|
bool ParseDriverVersion(const OUString& aVersion, uint64_t& rNumericVersion)
|
|
{
|
|
rNumericVersion = 0;
|
|
|
|
#if defined(_WIN32)
|
|
int a, b, c, d;
|
|
char aStr[8], bStr[8], cStr[8], dStr[8];
|
|
/* honestly, why do I even bother */
|
|
OString aOVersion = OUStringToOString(aVersion, RTL_TEXTENCODING_UTF8);
|
|
if (!SplitDriverVersion(aOVersion.getStr(), aStr, bStr, cStr, dStr))
|
|
return false;
|
|
|
|
PadDriverDecimal(bStr);
|
|
PadDriverDecimal(cStr);
|
|
PadDriverDecimal(dStr);
|
|
|
|
a = atoi(aStr);
|
|
b = atoi(bStr);
|
|
c = atoi(cStr);
|
|
d = atoi(dStr);
|
|
|
|
if (a < 0 || a > 0xffff) return false;
|
|
if (b < 0 || b > 0xffff) return false;
|
|
if (c < 0 || c > 0xffff) return false;
|
|
if (d < 0 || d > 0xffff) return false;
|
|
|
|
rNumericVersion = GFX_DRIVER_VERSION(a, b, c, d);
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
uint64_t DriverInfo::allDriverVersions = ~(uint64_t(0));
|
|
|
|
DriverInfo::DriverInfo()
|
|
: meOperatingSystem(wgl::DRIVER_OS_UNKNOWN),
|
|
mnOperatingSystemVersion(0),
|
|
maAdapterVendor(WinOpenGLDeviceInfo::GetDeviceVendor(VendorAll)),
|
|
mbWhitelisted(false),
|
|
meComparisonOp(DRIVER_COMPARISON_IGNORED),
|
|
mnDriverVersion(0),
|
|
mnDriverVersionMax(0)
|
|
{}
|
|
|
|
DriverInfo::DriverInfo(OperatingSystem os, const OUString& vendor,
|
|
VersionComparisonOp op,
|
|
uint64_t driverVersion,
|
|
bool bWhitelisted,
|
|
const char *suggestedVersion /* = nullptr */)
|
|
: meOperatingSystem(os),
|
|
mnOperatingSystemVersion(0),
|
|
maAdapterVendor(vendor),
|
|
mbWhitelisted(bWhitelisted),
|
|
meComparisonOp(op),
|
|
mnDriverVersion(driverVersion),
|
|
mnDriverVersionMax(0)
|
|
{
|
|
if (suggestedVersion)
|
|
maSuggestedVersion = OStringToOUString(OString(suggestedVersion), RTL_TEXTENCODING_UTF8);
|
|
}
|
|
|
|
DriverInfo::~DriverInfo()
|
|
{
|
|
}
|
|
|
|
}
|
|
|
|
WinOpenGLDeviceInfo::WinOpenGLDeviceInfo():
|
|
mbHasDualGPU(false),
|
|
mbRDP(false)
|
|
{
|
|
GetData();
|
|
FillBlacklist();
|
|
}
|
|
|
|
WinOpenGLDeviceInfo::~WinOpenGLDeviceInfo()
|
|
{
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct compareIgnoreAsciiCase
|
|
{
|
|
explicit compareIgnoreAsciiCase(const OUString& rString)
|
|
: maString(rString)
|
|
{
|
|
}
|
|
|
|
bool operator()(const OUString& rCompare)
|
|
{
|
|
return maString.equalsIgnoreAsciiCase(rCompare);
|
|
}
|
|
|
|
private:
|
|
OUString maString;
|
|
};
|
|
|
|
}
|
|
|
|
bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList(std::vector<wgl::DriverInfo>& aDeviceInfos,
|
|
OUString sDriverVersion, OUString sAdapterVendorID,
|
|
OUString sAdapterDeviceID, uint32_t nWindowsVersion)
|
|
{
|
|
uint64_t driverVersion;
|
|
wgl::ParseDriverVersion(sDriverVersion, driverVersion);
|
|
|
|
wgl::OperatingSystem eOS = WindowsVersionToOperatingSystem(nWindowsVersion);
|
|
bool match = false;
|
|
uint32_t i = 0;
|
|
for (; i < aDeviceInfos.size(); i++)
|
|
{
|
|
if (aDeviceInfos[i].meOperatingSystem != wgl::DRIVER_OS_ALL &&
|
|
aDeviceInfos[i].meOperatingSystem != eOS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (aDeviceInfos[i].mnOperatingSystemVersion && aDeviceInfos[i].mnOperatingSystemVersion != nWindowsVersion)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(GetDeviceVendor(wgl::VendorAll)) &&
|
|
!aDeviceInfos[i].maAdapterVendor.equalsIgnoreAsciiCase(sAdapterVendorID))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(), compareIgnoreAsciiCase("all")) &&
|
|
std::none_of(aDeviceInfos[i].maDevices.begin(), aDeviceInfos[i].maDevices.end(), compareIgnoreAsciiCase(sAdapterDeviceID)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (aDeviceInfos[i].meComparisonOp)
|
|
{
|
|
case wgl::DRIVER_LESS_THAN:
|
|
match = driverVersion < aDeviceInfos[i].mnDriverVersion;
|
|
break;
|
|
case wgl::DRIVER_LESS_THAN_OR_EQUAL:
|
|
match = driverVersion <= aDeviceInfos[i].mnDriverVersion;
|
|
break;
|
|
case wgl::DRIVER_GREATER_THAN:
|
|
match = driverVersion > aDeviceInfos[i].mnDriverVersion;
|
|
break;
|
|
case wgl::DRIVER_GREATER_THAN_OR_EQUAL:
|
|
match = driverVersion >= aDeviceInfos[i].mnDriverVersion;
|
|
break;
|
|
case wgl::DRIVER_EQUAL:
|
|
match = driverVersion == aDeviceInfos[i].mnDriverVersion;
|
|
break;
|
|
case wgl::DRIVER_NOT_EQUAL:
|
|
match = driverVersion != aDeviceInfos[i].mnDriverVersion;
|
|
break;
|
|
case wgl::DRIVER_BETWEEN_EXCLUSIVE:
|
|
match = driverVersion > aDeviceInfos[i].mnDriverVersion && driverVersion < aDeviceInfos[i].mnDriverVersionMax;
|
|
break;
|
|
case wgl::DRIVER_BETWEEN_INCLUSIVE:
|
|
match = driverVersion >= aDeviceInfos[i].mnDriverVersion && driverVersion <= aDeviceInfos[i].mnDriverVersionMax;
|
|
break;
|
|
case wgl::DRIVER_BETWEEN_INCLUSIVE_START:
|
|
match = driverVersion >= aDeviceInfos[i].mnDriverVersion && driverVersion < aDeviceInfos[i].mnDriverVersionMax;
|
|
break;
|
|
case wgl::DRIVER_COMPARISON_IGNORED:
|
|
// We don't have a comparison op, so we match everything.
|
|
match = true;
|
|
break;
|
|
default:
|
|
SAL_WARN("vcl.opengl", "Bogus op in GfxDriverInfo");
|
|
break;
|
|
}
|
|
|
|
if (match || aDeviceInfos[i].mnDriverVersion == wgl::DriverInfo::allDriverVersions)
|
|
{
|
|
// white listed drivers
|
|
if (aDeviceInfos[i].mbWhitelisted)
|
|
{
|
|
SAL_WARN("vcl.opengl", "whitelisted driver");
|
|
return false;
|
|
}
|
|
|
|
match = true;
|
|
SAL_WARN("vcl.opengl", "use : " << aDeviceInfos[i].maSuggestedVersion);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAL_INFO("vcl.opengl", (match ? "BLACKLISTED" : "not blacklisted"));
|
|
return match;
|
|
}
|
|
|
|
bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList()
|
|
{
|
|
return FindBlocklistedDeviceInList(maDriverInfo, maDriverVersion, maAdapterVendorID, maAdapterDeviceID, mnWindowsVersion);
|
|
}
|
|
|
|
namespace {
|
|
|
|
OUString getCacheFolder()
|
|
{
|
|
OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/");
|
|
rtl::Bootstrap::expandMacros(url);
|
|
|
|
osl::Directory::create(url);
|
|
|
|
return url;
|
|
}
|
|
|
|
void writeToLog(SvStream& rStrm, const char* pKey, const OUString rVal)
|
|
{
|
|
rStrm.WriteCharPtr(pKey);
|
|
rStrm.WriteCharPtr(": ");
|
|
rStrm.WriteOString(OUStringToOString(rVal, RTL_TEXTENCODING_UTF8));
|
|
rStrm.WriteChar('\n');
|
|
}
|
|
|
|
}
|
|
|
|
bool WinOpenGLDeviceInfo::isDeviceBlocked()
|
|
{
|
|
CrashReporter::AddKeyValue("OpenGLVendor", maAdapterVendorID);
|
|
CrashReporter::AddKeyValue("OpenGLDevice", maAdapterDeviceID);
|
|
CrashReporter::AddKeyValue("OpenGLDriver", maDriverVersion);
|
|
|
|
SAL_INFO("vcl.opengl", maDriverVersion);
|
|
SAL_INFO("vcl.opengl", maDriverDate);
|
|
SAL_INFO("vcl.opengl", maDeviceID);
|
|
SAL_INFO("vcl.opengl", maAdapterVendorID);
|
|
SAL_INFO("vcl.opengl", maAdapterDeviceID);
|
|
SAL_INFO("vcl.opengl", maAdapterSubsysID);
|
|
SAL_INFO("vcl.opengl", maDeviceKey);
|
|
SAL_INFO("vcl.opengl", maDeviceString);
|
|
|
|
OUString aCacheFolder = getCacheFolder();
|
|
|
|
OUString aCacheFile(aCacheFolder + "/opengl_device.log");
|
|
SvFileStream aOpenGLLogFile(aCacheFile, StreamMode::WRITE);
|
|
|
|
writeToLog(aOpenGLLogFile, "DriverVersion", maDriverVersion);
|
|
writeToLog(aOpenGLLogFile, "DriverDate", maDriverDate);
|
|
writeToLog(aOpenGLLogFile, "DeviceID", maDeviceID);
|
|
writeToLog(aOpenGLLogFile, "AdapterVendorID", maAdapterVendorID);
|
|
writeToLog(aOpenGLLogFile, "AdapterDeviceID", maAdapterDeviceID);
|
|
writeToLog(aOpenGLLogFile, "AdapterSubsysID", maAdapterSubsysID);
|
|
writeToLog(aOpenGLLogFile, "DeviceKey", maDeviceKey);
|
|
writeToLog(aOpenGLLogFile, "DeviceString", maDeviceString);
|
|
|
|
// Check if the device is blocked from the downloaded blocklist. If not, check
|
|
// the static list after that. This order is used so that we can later escape
|
|
// out of static blocks (i.e. if we were wrong or something was patched, we
|
|
// can back out our static block without doing a release).
|
|
if (mbRDP)
|
|
{
|
|
SAL_WARN("vcl.opengl", "all OpenGL blocked for RDP sessions");
|
|
return true;
|
|
}
|
|
|
|
return FindBlocklistedDeviceInList();
|
|
}
|
|
|
|
void WinOpenGLDeviceInfo::GetData()
|
|
{
|
|
DISPLAY_DEVICEW displayDevice;
|
|
displayDevice.cb = sizeof(displayDevice);
|
|
|
|
mnWindowsVersion = WindowsOSVersion();
|
|
int deviceIndex = 0;
|
|
|
|
while (EnumDisplayDevicesW(nullptr, deviceIndex, &displayDevice, 0))
|
|
{
|
|
if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
break;
|
|
}
|
|
deviceIndex++;
|
|
}
|
|
|
|
// make sure the string is null terminated
|
|
// (using the term "null" here to mean a zero UTF-16 unit)
|
|
if (wcsnlen(displayDevice.DeviceKey, ArrayLength(displayDevice.DeviceKey))
|
|
== ArrayLength(displayDevice.DeviceKey))
|
|
{
|
|
// we did not find a null
|
|
SAL_WARN("vcl.opengl", "string not null terminated");
|
|
return;
|
|
}
|
|
|
|
/* DeviceKey is "reserved" according to MSDN so we'll be careful with it */
|
|
/* check that DeviceKey begins with DEVICE_KEY_PREFIX */
|
|
/* some systems have a DeviceKey starting with \REGISTRY\Machine\ so we need to compare case insensitively */
|
|
if (_wcsnicmp(displayDevice.DeviceKey, DEVICE_KEY_PREFIX, ArrayLength(DEVICE_KEY_PREFIX)-1) != 0)
|
|
{
|
|
SAL_WARN("vcl.opengl", "incorrect DeviceKey");
|
|
return;
|
|
}
|
|
|
|
// chop off DEVICE_KEY_PREFIX
|
|
maDeviceKey = displayDevice.DeviceKey + ArrayLength(DEVICE_KEY_PREFIX)-1;
|
|
|
|
maDeviceID = displayDevice.DeviceID;
|
|
maDeviceString = displayDevice.DeviceString;
|
|
|
|
if (maDeviceID.isEmpty() &&
|
|
(maDeviceString == "RDPDD Chained DD" ||
|
|
(maDeviceString == "RDPUDD Chained DD")))
|
|
{
|
|
// we need to block RDP as it does not provide OpenGL 2.1+
|
|
mbRDP = true;
|
|
SAL_WARN("vcl.opengl", "RDP => blocked");
|
|
return;
|
|
}
|
|
|
|
/* create a device information set composed of the current display device */
|
|
HDEVINFO devinfo = SetupDiGetClassDevsW(nullptr, maDeviceID.getStr(), nullptr,
|
|
DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
|
|
|
|
if (devinfo != INVALID_HANDLE_VALUE)
|
|
{
|
|
HKEY key;
|
|
LONG result;
|
|
WCHAR value[255];
|
|
DWORD dwcbData;
|
|
SP_DEVINFO_DATA devinfoData;
|
|
DWORD memberIndex = 0;
|
|
|
|
devinfoData.cbSize = sizeof(devinfoData);
|
|
OUString aDriverKeyPre("System\\CurrentControlSet\\Control\\Class\\");
|
|
/* enumerate device information elements in the device information set */
|
|
while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
|
|
{
|
|
/* get a string that identifies the device's driver key */
|
|
if (SetupDiGetDeviceRegistryPropertyW(devinfo,
|
|
&devinfoData,
|
|
SPDRP_DRIVER,
|
|
nullptr,
|
|
(PBYTE)value,
|
|
sizeof(value),
|
|
nullptr))
|
|
{
|
|
OUString driverKey(aDriverKeyPre);
|
|
driverKey += value;
|
|
result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, driverKey.getStr(), 0, KEY_QUERY_VALUE, &key);
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
/* we've found the driver we're looking for */
|
|
dwcbData = sizeof(value);
|
|
result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
|
|
(LPBYTE)value, &dwcbData);
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
maDriverVersion = OUString(value);
|
|
}
|
|
else
|
|
{
|
|
// If the entry wasn't found, assume the worst (0.0.0.0).
|
|
maDriverVersion = OUString("0.0.0.0");
|
|
}
|
|
dwcbData = sizeof(value);
|
|
result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
|
|
(LPBYTE)value, &dwcbData);
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
maDriverDate = value;
|
|
}
|
|
else
|
|
{
|
|
// Again, assume the worst
|
|
maDriverDate = OUString("01-01-1970");
|
|
}
|
|
RegCloseKey(key);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetupDiDestroyDeviceInfoList(devinfo);
|
|
}
|
|
else
|
|
{
|
|
SAL_WARN("vcl.opengl", "invalid handle value");
|
|
}
|
|
|
|
appendIntegerWithPadding(maAdapterVendorID, ParseIDFromDeviceID(maDeviceID, "VEN_", 4), 4);
|
|
appendIntegerWithPadding(maAdapterDeviceID, ParseIDFromDeviceID(maDeviceID, "&DEV_", 4), 4);
|
|
appendIntegerWithPadding(maAdapterSubsysID, ParseIDFromDeviceID(maDeviceID, "&SUBSYS_", 8), 8);
|
|
|
|
// We now check for second display adapter.
|
|
|
|
// Device interface class for display adapters.
|
|
CLSID GUID_DISPLAY_DEVICE_ARRIVAL;
|
|
HRESULT hresult = CLSIDFromString(L"{1CA05180-A699-450A-9A0C-DE4FBE3DDD89}",
|
|
&GUID_DISPLAY_DEVICE_ARRIVAL);
|
|
if (hresult == NOERROR)
|
|
{
|
|
devinfo = SetupDiGetClassDevsW(&GUID_DISPLAY_DEVICE_ARRIVAL,
|
|
nullptr, nullptr,
|
|
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
|
|
|
|
if (devinfo != INVALID_HANDLE_VALUE)
|
|
{
|
|
HKEY key;
|
|
LONG result;
|
|
WCHAR value[255];
|
|
DWORD dwcbData;
|
|
SP_DEVINFO_DATA devinfoData;
|
|
DWORD memberIndex = 0;
|
|
devinfoData.cbSize = sizeof(devinfoData);
|
|
|
|
OUString aAdapterDriver2;
|
|
OUString aDeviceID2;
|
|
OUString aDriverVersion2;
|
|
OUString aDriverDate2;
|
|
uint32_t adapterVendorID2;
|
|
uint32_t adapterDeviceID2;
|
|
|
|
OUString aDriverKeyPre("System\\CurrentControlSet\\Control\\Class\\");
|
|
/* enumerate device information elements in the device information set */
|
|
while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
|
|
{
|
|
/* get a string that identifies the device's driver key */
|
|
if (SetupDiGetDeviceRegistryPropertyW(devinfo,
|
|
&devinfoData,
|
|
SPDRP_DRIVER,
|
|
nullptr,
|
|
(PBYTE)value,
|
|
sizeof(value),
|
|
nullptr))
|
|
{
|
|
OUString driverKey2(aDriverKeyPre);
|
|
driverKey2 += value;
|
|
result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, driverKey2.getStr(), 0, KEY_QUERY_VALUE, &key);
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
dwcbData = sizeof(value);
|
|
result = RegQueryValueExW(key, L"MatchingDeviceId", nullptr,
|
|
nullptr, (LPBYTE)value, &dwcbData);
|
|
if (result != ERROR_SUCCESS)
|
|
{
|
|
continue;
|
|
}
|
|
aDeviceID2 = value;
|
|
OUString aAdapterVendorID2String;
|
|
OUString aAdapterDeviceID2String;
|
|
adapterVendorID2 = ParseIDFromDeviceID(aDeviceID2, "VEN_", 4);
|
|
appendIntegerWithPadding(aAdapterVendorID2String, adapterVendorID2, 4);
|
|
adapterDeviceID2 = ParseIDFromDeviceID(aDeviceID2, "&DEV_", 4);
|
|
appendIntegerWithPadding(aAdapterDeviceID2String, adapterDeviceID2, 4);
|
|
if (maAdapterVendorID == aAdapterVendorID2String &&
|
|
maAdapterDeviceID == aAdapterDeviceID2String)
|
|
{
|
|
RegCloseKey(key);
|
|
continue;
|
|
}
|
|
|
|
// If this device is missing driver information, it is unlikely to
|
|
// be a real display adapter.
|
|
if (!GetKeyValue(driverKey2.getStr(), L"InstalledDisplayDrivers",
|
|
aAdapterDriver2, REG_MULTI_SZ))
|
|
{
|
|
RegCloseKey(key);
|
|
continue;
|
|
}
|
|
dwcbData = sizeof(value);
|
|
result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
|
|
(LPBYTE)value, &dwcbData);
|
|
if (result != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(key);
|
|
continue;
|
|
}
|
|
aDriverVersion2 = value;
|
|
dwcbData = sizeof(value);
|
|
result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
|
|
(LPBYTE)value, &dwcbData);
|
|
if (result != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(key);
|
|
continue;
|
|
}
|
|
aDriverDate2 = value;
|
|
dwcbData = sizeof(value);
|
|
result = RegQueryValueExW(key, L"Device Description", nullptr,
|
|
nullptr, (LPBYTE)value, &dwcbData);
|
|
if (result != ERROR_SUCCESS)
|
|
{
|
|
dwcbData = sizeof(value);
|
|
result = RegQueryValueExW(key, L"DriverDesc", nullptr, nullptr,
|
|
(LPBYTE)value, &dwcbData);
|
|
}
|
|
RegCloseKey(key);
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
mbHasDualGPU = true;
|
|
maDeviceString2 = value;
|
|
maDeviceID2 = aDeviceID2;
|
|
maDeviceKey2 = driverKey2;
|
|
maDriverVersion2 = aDriverVersion2;
|
|
maDriverDate2 = aDriverDate2;
|
|
appendIntegerWithPadding(maAdapterVendorID2, adapterVendorID2, 4);
|
|
appendIntegerWithPadding(maAdapterDeviceID2, adapterDeviceID2, 4);
|
|
appendIntegerWithPadding(maAdapterSubsysID2, ParseIDFromDeviceID(maDeviceID2, "&SUBSYS_", 8), 8);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SetupDiDestroyDeviceInfoList(devinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Macro for assigning a device vendor id to a string.
|
|
#define DECLARE_VENDOR_ID(name, deviceId) \
|
|
case name: \
|
|
*mpDeviceVendors[id] = OUString(deviceId); \
|
|
break;
|
|
|
|
OUString WinOpenGLDeviceInfo::GetDeviceVendor(wgl::DeviceVendor id)
|
|
{
|
|
assert(id >= 0 && id < wgl::DeviceVendorMax);
|
|
|
|
if (mpDeviceVendors[id])
|
|
return *mpDeviceVendors[id];
|
|
|
|
mpDeviceVendors[id] = new OUString();
|
|
|
|
switch (id)
|
|
{
|
|
DECLARE_VENDOR_ID(wgl::VendorAll, "");
|
|
DECLARE_VENDOR_ID(wgl::VendorIntel, "0x8086");
|
|
DECLARE_VENDOR_ID(wgl::VendorNVIDIA, "0x10de");
|
|
DECLARE_VENDOR_ID(wgl::VendorAMD, "0x1022");
|
|
DECLARE_VENDOR_ID(wgl::VendorATI, "0x1002");
|
|
DECLARE_VENDOR_ID(wgl::VendorMicrosoft, "0x1414");
|
|
// Suppress a warning.
|
|
DECLARE_VENDOR_ID(wgl::DeviceVendorMax, "");
|
|
}
|
|
|
|
return *mpDeviceVendors[id];
|
|
}
|
|
|
|
namespace {
|
|
|
|
|
|
OUString getBlacklistFile()
|
|
{
|
|
OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER);
|
|
rtl::Bootstrap::expandMacros(url);
|
|
|
|
return url + "/opengl/opengl_blacklist_windows.xml";
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void WinOpenGLDeviceInfo::FillBlacklist()
|
|
{
|
|
OUString aURL = getBlacklistFile();
|
|
WinBlocklistParser aParser(aURL, maDriverInfo);
|
|
try {
|
|
aParser.parse();
|
|
}
|
|
catch (...)
|
|
{
|
|
SAL_WARN("vcl.opengl.win", "error parsing blacklist");
|
|
maDriverInfo.clear();
|
|
}
|
|
}
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|