diff options
Diffstat (limited to 'src/plugins')
11 files changed, 205 insertions, 22 deletions
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index bba7d044f7..e29e5b8187 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -378,6 +378,11 @@ void QWindowsContext::setTabletAbsoluteRange(int a) #endif } +void QWindowsContext::setDetectAltGrModifier(bool a) +{ + d->m_keyMapper.setDetectAltGrModifier(a); +} + int QWindowsContext::processDpiAwareness() { int result; diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index fe83c83934..3709d9deee 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -208,6 +208,8 @@ public: void setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness); static int processDpiAwareness(); + void setDetectAltGrModifier(bool a); + // Returns a combination of SystemInfoFlags unsigned systemInfo() const; diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 1a1d51cae1..4824de5c9c 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -198,6 +198,8 @@ static inline unsigned parseOptions(const QStringList ¶mList, } else if (param.endsWith(QLatin1String("none"))) { options |= QWindowsIntegration::NoNativeDialogs; } + } else if (param == QLatin1String("altgr")) { + options |= QWindowsIntegration::DetectAltGrModifier; } else if (param == QLatin1String("gl=gdi")) { options |= QWindowsIntegration::DisableArb; } else if (param == QLatin1String("nodirectwrite")) { @@ -269,6 +271,7 @@ QWindowsIntegration::QWindowsIntegration(const QStringList ¶mList) : d->m_clipboard.registerViewer(); #endif d->m_context.screenManager().handleScreenChanges(); + d->m_context.setDetectAltGrModifier((d->m_options & DetectAltGrModifier) != 0); } QWindowsIntegration::~QWindowsIntegration() diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index 25f485679d..da86852766 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -69,6 +69,7 @@ public: AlwaysUseNativeMenus = 0x100, NoNativeMenus = 0x200, DontUseWMPointer = 0x400, + DetectAltGrModifier = 0x800 }; explicit QWindowsIntegration(const QStringList ¶mList); diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index e7efd6e057..1209b6c4b4 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -672,6 +672,7 @@ void QWindowsKeyMapper::changeKeyboard() bidi = true; keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight; + m_seenAltGr = false; } // Helper function that is used when obtaining the list of characters that can be produced by one key and @@ -906,8 +907,34 @@ bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, con #endif } -bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &msg, bool /* grab */, LRESULT *lResult) +// QTBUG-69317: Check for AltGr found on some keyboards +// which is a sequence of left Ctrl (SYSKEY) + right Menu (Alt). +static bool isAltGr(MSG *msg) { + enum : LONG_PTR { RightFlag = 0x1000000 }; + if (msg->wParam != VK_CONTROL || (msg->lParam & RightFlag) != 0 + || (msg->message != WM_KEYDOWN && msg->message != WM_SYSKEYUP)) { + return false; + } + const UINT expectedMessage = msg->message == WM_SYSKEYUP + ? WM_KEYUP : msg->message; + MSG peekedMsg; + if (PeekMessage(&peekedMsg, msg->hwnd, 0, 0, PM_NOREMOVE) == FALSE + || peekedMsg.message != expectedMessage || peekedMsg.wParam != VK_MENU + || (peekedMsg.lParam & RightFlag) == 0) { + return false; + } + *msg = peekedMsg; + PeekMessage(&peekedMsg, msg->hwnd, 0, 0, PM_REMOVE); + return true; +} + +bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, + bool /* grab */, LRESULT *lResult) +{ + const bool altGr = m_detectAltGrModifier && isAltGr(&msg); + if (altGr) + m_seenAltGr = true; const UINT msgType = msg.message; const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; @@ -936,10 +963,12 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms // Get the modifier states (may be altered later, depending on key code) int state = 0; state |= (nModifiers & ShiftAny ? int(Qt::ShiftModifier) : 0); - state |= (nModifiers & ControlAny ? int(Qt::ControlModifier) : 0); - state |= (nModifiers & AltAny ? int(Qt::AltModifier) : 0); + state |= (nModifiers & AltLeft ? int(Qt::AltModifier) : 0); + if ((nModifiers & AltRight) != 0) + state |= m_seenAltGr ? Qt::GroupSwitchModifier : Qt::AltModifier; + if ((nModifiers & ControlAny) != 0 && (state & Qt::GroupSwitchModifier) == 0) + state |= Qt::ControlModifier; state |= (nModifiers & MetaAny ? int(Qt::MetaModifier) : 0); - // A multi-character key or a Input method character // not found by our look-ahead if (msgType == WM_CHAR || msgType == WM_IME_CHAR) { @@ -1010,8 +1039,17 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms modifiersIndex |= (nModifiers & ControlAny ? 0x2 : 0); modifiersIndex |= (nModifiers & AltAny ? 0x4 : 0); + // Note: For the resulting key, AltGr is equivalent to Alt + Ctrl (as + // opposed to Linux); hence no entry in KeyboardLayoutItem is required int code = keyLayout[vk_key].qtKey[modifiersIndex]; + // If the bit 24 of lParm is set you received a enter, + // otherwise a Return. (This is the extended key bit) + if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000)) + code = Qt::Key_Enter; + else if (altGr) + code = Qt::Key_AltGr; + // Invert state logic: // If the key actually pressed is a modifier key, then we remove its modifier key from the // state, since a modifier-key can't have itself as a modifier @@ -1021,11 +1059,8 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms state = state ^ Qt::ShiftModifier; else if (code == Qt::Key_Alt) state = state ^ Qt::AltModifier; - - // If the bit 24 of lParm is set you received a enter, - // otherwise a Return. (This is the extended key bit) - if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000)) - code = Qt::Key_Enter; + else if (code == Qt::Key_AltGr) + state = state ^ Qt::GroupSwitchModifier; // All cursor keys without extended bit if (!(msg.lParam & 0x1000000)) { diff --git a/src/plugins/platforms/windows/qwindowskeymapper.h b/src/plugins/platforms/windows/qwindowskeymapper.h index d569c82437..a454f0f973 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.h +++ b/src/plugins/platforms/windows/qwindowskeymapper.h @@ -81,6 +81,9 @@ public: void setUseRTLExtensions(bool e) { m_useRTLExtensions = e; } bool useRTLExtensions() const { return m_useRTLExtensions; } + void setDetectAltGrModifier(bool a) { m_detectAltGrModifier = a; } + bool detectAltGrModifier() const { return m_detectAltGrModifier; } + bool translateKeyEvent(QWindow *widget, HWND hwnd, const MSG &msg, LRESULT *result); QWindow *keyGrabber() const { return m_keyGrabber; } @@ -90,7 +93,7 @@ public: QList<int> possibleKeys(const QKeyEvent *e) const; private: - bool translateKeyEventInternal(QWindow *receiver, const MSG &msg, bool grab, LRESULT *lResult); + bool translateKeyEventInternal(QWindow *receiver, MSG msg, bool grab, LRESULT *lResult); bool translateMultimediaKeyEventInternal(QWindow *receiver, const MSG &msg); void updateKeyMap(const MSG &msg); @@ -106,6 +109,9 @@ private: QChar m_lastHighSurrogate; static const size_t NumKeyboardLayoutItems = 256; KeyboardLayoutItem keyLayout[NumKeyboardLayoutItems]; + bool m_detectAltGrModifier = false; + bool m_seenAltGr = false; + }; enum WindowsNativeModifiers { 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() diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp index 11397fb9ec..7a91c3d4e2 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp @@ -180,7 +180,7 @@ long roleToControlTypeId(QAccessible::Role role) {QAccessible::PropertyPage, UIA_CustomControlTypeId}, {QAccessible::Indicator, UIA_CustomControlTypeId}, {QAccessible::Graphic, UIA_ImageControlTypeId}, - {QAccessible::StaticText, UIA_EditControlTypeId}, + {QAccessible::StaticText, UIA_TextControlTypeId}, {QAccessible::EditableText, UIA_EditControlTypeId}, {QAccessible::Button, UIA_ButtonControlTypeId}, {QAccessible::CheckBox, UIA_CheckBoxControlTypeId}, diff --git a/src/plugins/printsupport/cups/qcupsprintersupport.cpp b/src/plugins/printsupport/cups/qcupsprintersupport.cpp index 56191c4dec..19e1df31f6 100644 --- a/src/plugins/printsupport/cups/qcupsprintersupport.cpp +++ b/src/plugins/printsupport/cups/qcupsprintersupport.cpp @@ -47,12 +47,14 @@ #include <QtPrintSupport/QPrinterInfo> +#if QT_CONFIG(dialogbuttonbox) #include <QGuiApplication> #include <QDialog> #include <QDialogButtonBox> #include <QFormLayout> #include <QLabel> #include <QLineEdit> +#endif // QT_CONFIG(dialogbuttonbox) #include <cups/ppd.h> #ifndef QT_LINUXBASE // LSB merges everything into cups.h @@ -61,6 +63,7 @@ QT_BEGIN_NAMESPACE +#if QT_CONFIG(dialogbuttonbox) static const char *getPasswordCB(const char */*prompt*/, http_t *http, const char */*method*/, const char *resource, void */*user_data*/) { // cups doesn't free the const char * we return so keep around @@ -122,13 +125,16 @@ static const char *getPasswordCB(const char */*prompt*/, http_t *http, const cha return password.constData(); } +#endif // QT_CONFIG(dialogbuttonbox) QCupsPrinterSupport::QCupsPrinterSupport() : QPlatformPrinterSupport() { +#if QT_CONFIG(dialogbuttonbox) // Only show password dialog if GUI application if (qobject_cast<QGuiApplication*>(QCoreApplication::instance())) cupsSetPasswordCB2(getPasswordCB, nullptr /* user_data */ ); +#endif // QT_CONFIG(dialogbuttonbox) } QCupsPrinterSupport::~QCupsPrinterSupport() |