diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/windows/qwindowsopengltester.cpp | 62 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsopengltester.h | 1 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowswindow.cpp | 84 |
3 files changed, 136 insertions, 11 deletions
diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp index c4ee820211..6af9f168a5 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.cpp +++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp @@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE +static const DWORD VENDOR_ID_AMD = 0x1002; + GpuDescription GpuDescription::detect() { typedef IDirect3D9 * (WINAPI *PtrDirect3DCreate9)(UINT); @@ -74,9 +76,16 @@ GpuDescription GpuDescription::detect() IDirect3D9 *direct3D9 = direct3DCreate9(D3D_SDK_VERSION); if (!direct3D9) return result; + D3DADAPTER_IDENTIFIER9 adapterIdentifier; - const HRESULT hr = direct3D9->GetAdapterIdentifier(0, 0, &adapterIdentifier); - direct3D9->Release(); + bool isAMD = false; + // Adapter "0" is D3DADAPTER_DEFAULT which returns the default adapter. In + // multi-GPU, multi-screen setups this is the GPU that is associated with + // the "main display" in the Display Settings, and this is the GPU OpenGL + // and D3D uses by default. Therefore querying any additional adapters is + // futile and not useful for our purposes in general, except for + // identifying a few special cases later on. + HRESULT hr = direct3D9->GetAdapterIdentifier(0, 0, &adapterIdentifier); if (SUCCEEDED(hr)) { result.vendorId = adapterIdentifier.VendorId; result.deviceId = adapterIdentifier.DeviceId; @@ -90,7 +99,37 @@ GpuDescription GpuDescription::detect() result.driverVersion = QVersionNumber(version); result.driverName = adapterIdentifier.Driver; result.description = adapterIdentifier.Description; + isAMD = result.vendorId == VENDOR_ID_AMD; } + + // Detect QTBUG-50371 (having AMD as the default adapter results in a crash + // when starting apps on a screen connected to the Intel card) by looking + // for a default AMD adapter and an additional non-AMD one. + if (isAMD) { + const UINT adapterCount = direct3D9->GetAdapterCount(); + for (UINT adp = 1; adp < adapterCount; ++adp) { + hr = direct3D9->GetAdapterIdentifier(adp, 0, &adapterIdentifier); + if (SUCCEEDED(hr)) { + if (adapterIdentifier.VendorId != VENDOR_ID_AMD) { + // Bingo. Now figure out the display for the AMD card. + DISPLAY_DEVICE dd; + memset(&dd, 0, sizeof(dd)); + dd.cb = sizeof(dd); + for (int dev = 0; EnumDisplayDevices(nullptr, dev, &dd, 0); ++dev) { + if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { + // DeviceName is something like \\.\DISPLAY1 which can be used to + // match with the MONITORINFOEX::szDevice queried by QWindowsScreen. + result.gpuSuitableScreen = QString::fromWCharArray(dd.DeviceName); + break; + } + } + break; + } + } + } + } + + direct3D9->Release(); return result; } @@ -103,7 +142,8 @@ QDebug operator<<(QDebug d, const GpuDescription &gd) << ", deviceId=" << gd.deviceId << ", subSysId=" << gd.subSysId << dec << noshowbase << ", revision=" << gd.revision << ", driver: " << gd.driverName - << ", version=" << gd.driverVersion << ", " << gd.description << ')'; + << ", version=" << gd.driverVersion << ", " << gd.description + << gd.gpuSuitableScreen << ')'; return d; } #endif // !QT_NO_DEBUG_STREAM @@ -113,15 +153,17 @@ QString GpuDescription::toString() const { QString result; QTextStream str(&result); - str << " Card name: " << description - << "\n Driver Name: " << driverName - << "\n Driver Version: " << driverVersion.toString() - << "\n Vendor ID: 0x" << qSetPadChar(QLatin1Char('0')) + str << " Card name : " << description + << "\n Driver Name : " << driverName + << "\n Driver Version : " << driverVersion.toString() + << "\n Vendor ID : 0x" << qSetPadChar(QLatin1Char('0')) << uppercasedigits << hex << qSetFieldWidth(4) << vendorId - << "\n Device ID: 0x" << qSetFieldWidth(4) << deviceId - << "\n SubSys ID: 0x" << qSetFieldWidth(8) << subSysId - << "\n Revision ID: 0x" << qSetFieldWidth(4) << revision + << "\n Device ID : 0x" << qSetFieldWidth(4) << deviceId + << "\n SubSys ID : 0x" << qSetFieldWidth(8) << subSysId + << "\n Revision ID : 0x" << qSetFieldWidth(4) << revision << dec; + if (!gpuSuitableScreen.isEmpty()) + str << "\nGL windows forced to screen: " << gpuSuitableScreen; return result; } diff --git a/src/plugins/platforms/windows/qwindowsopengltester.h b/src/plugins/platforms/windows/qwindowsopengltester.h index 5ee2508462..22170f30b0 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.h +++ b/src/plugins/platforms/windows/qwindowsopengltester.h @@ -62,6 +62,7 @@ struct GpuDescription QVersionNumber driverVersion; QByteArray driverName; QByteArray description; + QString gpuSuitableScreen; }; #ifndef QT_NO_DEBUG_STREAM diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 528927b0fb..9c31409644 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -58,6 +58,7 @@ #else # include "qwindowsopenglcontext.h" #endif +#include "qwindowsopengltester.h" #ifdef QT_NO_CURSOR # include "qwindowscursor.h" #endif @@ -541,6 +542,84 @@ static inline void fixTopLevelWindowFlags(Qt::WindowFlags &flags) flags |= Qt::FramelessWindowHint; } +static QScreen *screenForName(const QWindow *w, const QString &name) +{ + QScreen *winScreen = w ? w->screen() : QGuiApplication::primaryScreen(); + if (winScreen && winScreen->name() != name) { + const auto screens = winScreen->virtualSiblings(); + for (QScreen *screen : screens) { + if (screen->name() == name) + return screen; + } + } + return winScreen; +} + +static QScreen *forcedScreenForGLWindow(const QWindow *w) +{ + const QString forceToScreen = GpuDescription::detect().gpuSuitableScreen; + return forceToScreen.isEmpty() ? nullptr : screenForName(w, forceToScreen); +} + +static QPoint calcPosition(const QWindow *w, const QWindowCreationContextPtr &context, const QMargins &invMargins) +{ + const QPoint orgPos(context->frameX - invMargins.left(), context->frameY - invMargins.top()); + + if (!w || (!w->isTopLevel() && w->surfaceType() != QWindow::OpenGLSurface)) + return orgPos; + + // Workaround for QTBUG-50371 + const QScreen *screenForGL = forcedScreenForGLWindow(w); + if (!screenForGL) + return orgPos; + + const QPoint posFrame(context->frameX, context->frameY); + const QMargins margins = context->margins; + const QRect scrGeo = screenForGL->handle()->availableGeometry(); + + // Point is already in the required screen. + if (scrGeo.contains(orgPos)) + return orgPos; + + // If the visible part of the window is already in the + // required screen, just ignore the invisible offset. + if (scrGeo.contains(posFrame)) + return posFrame; + + // Find the original screen containing the coordinates. + const QList<QScreen *> screens = screenForGL->virtualSiblings(); + const QScreen *orgScreen = nullptr; + for (QScreen *screen : screens) { + if (screen->handle()->availableGeometry().contains(posFrame)) { + orgScreen = screen; + break; + } + } + const QPoint ctPos = QPoint(qMax(scrGeo.left(), scrGeo.center().x() + + (margins.right() - margins.left() - context->frameWidth)/2), + qMax(scrGeo.top(), scrGeo.center().y() + + (margins.bottom() - margins.top() - context->frameHeight)/2)); + + // If initial coordinates were outside all screens, center the window on the required screen. + if (!orgScreen) + return ctPos; + + const QRect orgGeo = orgScreen->handle()->availableGeometry(); + const QRect orgFrame(QPoint(context->frameX, context->frameY), + QSize(context->frameWidth, context->frameHeight)); + + // Window would be centered on orgScreen. Center it on the required screen. + if (orgGeo.center() == (orgFrame - margins).center()) + return ctPos; + + // Transform the coordinates to map them into the required screen. + const QPoint newPos(scrGeo.left() + ((posFrame.x() - orgGeo.left()) * scrGeo.width()) / orgGeo.width(), + scrGeo.top() + ((posFrame.y() - orgGeo.top()) * scrGeo.height()) / orgGeo.height()); + const QPoint newPosNoMargin(newPos.x() - invMargins.left(), newPos.y() - invMargins.top()); + + return scrGeo.contains(newPosNoMargin) ? newPosNoMargin : newPos; +} + void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn, unsigned creationFlags) { @@ -688,9 +767,12 @@ QWindowsWindowData << " custom margins: " << context->customMargins << " invisible margins: " << invMargins; + + QPoint pos = calcPosition(w, context, invMargins); + result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16, style, - context->frameX - invMargins.left(), context->frameY - invMargins.top(), + pos.x(), pos.y(), context->frameWidth, context->frameHeight, parentHandle, NULL, appinst, NULL); qCDebug(lcQpaWindows).nospace() |