diff options
Diffstat (limited to 'src/plugins/platforms/windows')
19 files changed, 353 insertions, 121 deletions
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h index 9a65603e24..75f984b4b0 100644 --- a/src/plugins/platforms/windows/qtwindowsglobal.h +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE namespace QtWindows { -enum +enum WindowsEventTypeFlags { WindowEventFlag = 0x10000, MouseEventFlag = 0x20000, @@ -146,6 +146,8 @@ enum WindowsEventType // Simplify event types GestureEvent = 124, UnknownEvent = 542 }; +Q_DECLARE_MIXED_ENUM_OPERATORS(bool, WindowsEventTypeFlags, WindowsEventType); +Q_DECLARE_MIXED_ENUM_OPERATORS(bool, WindowsEventType, WindowsEventTypeFlags); // Matches Process_DPI_Awareness (Windows 8.1 onwards), used for SetProcessDpiAwareness() enum ProcessDpiAwareness diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index d87804cda2..41d66bdee9 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -45,6 +45,7 @@ #include <QtCore/qscopedpointer.h> #include <QtCore/quuid.h> #include <QtCore/private/qwinregistry_p.h> +#include <QtCore/private/qfactorycacheregistration_p.h> #include <QtGui/private/qwindowsguieventdispatcher_p.h> @@ -221,8 +222,12 @@ QWindowsContext::~QWindowsContext() DestroyWindow(d->m_powerDummyWindow); unregisterWindowClasses(); - if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) + if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) { +#ifdef QT_USE_FACTORY_CACHE_REGISTRATION + detail::QWinRTFactoryCacheRegistration::clearAllCaches(); +#endif OleUninitialize(); + } d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows. if (d->m_displayContext) @@ -264,7 +269,7 @@ void QWindowsContext::registerTouchWindows() { if (QGuiApplicationPrivate::is_app_running && (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) != 0) { - for (QWindowsWindow *w : qAsConst(d->m_windows)) + for (QWindowsWindow *w : std::as_const(d->m_windows)) w->registerTouchWindow(); } } @@ -376,8 +381,8 @@ void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiA const HRESULT hr = SetProcessDpiAwareness(static_cast<PROCESS_DPI_AWARENESS>(dpiAwareness)); // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin). // Silence warning in that case unless debug is enabled. - if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) { - qWarning().noquote().nospace() << "SetProcessDpiAwareness(" + if (FAILED(hr) && hr != E_ACCESSDENIED) { + qCWarning(lcQpaWindows).noquote().nospace() << "SetProcessDpiAwareness(" << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr) << ", using " << QWindowsContext::processDpiAwareness(); } @@ -388,12 +393,16 @@ bool QWindowsContext::setProcessDpiV2Awareness() qCDebug(lcQpaWindows) << __FUNCTION__; const BOOL ok = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); if (!ok) { - const HRESULT errorCode = GetLastError(); - qCWarning(lcQpaWindows).noquote().nospace() << "setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: " - << QWindowsContext::comErrorString(errorCode); - return false; + const DWORD dwError = GetLastError(); + // ERROR_ACCESS_DENIED means set externally (MSVC manifest or external app loading Qt plugin). + // Silence warning in that case unless debug is enabled. + if (dwError != ERROR_ACCESS_DENIED) { + qCWarning(lcQpaWindows).noquote().nospace() + << "SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: " + << QWindowsContext::comErrorString(HRESULT(dwError)); + return false; + } } - QWindowsContextPrivate::m_v2DpiAware = true; return true; } @@ -601,7 +610,7 @@ void QWindowsContext::unregisterWindowClasses() { const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr)); - for (const QString &name : qAsConst(d->m_registeredWindowClassNames)) { + for (const QString &name : std::as_const(d->m_registeredWindowClassNames)) { if (!UnregisterClass(reinterpret_cast<LPCWSTR>(name.utf16()), appInstance) && QWindowsContext::verbose) qErrnoWarning("UnregisterClass failed for '%s'", qPrintable(name)); } @@ -733,6 +742,8 @@ static inline bool findPlatformWindowHelper(const POINT &screenPoint, unsigned c if (!(cwexFlags & CWP_SKIPTRANSPARENT) && (GetWindowLongPtr(child, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) { const HWND nonTransparentChild = ChildWindowFromPointEx(*hwnd, point, cwexFlags | CWP_SKIPTRANSPARENT); + if (!nonTransparentChild || nonTransparentChild == *hwnd) + return false; if (QWindowsWindow *nonTransparentWindow = context->findPlatformWindow(nonTransparentChild)) { *result = nonTransparentWindow; *hwnd = nonTransparentChild; diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index af92690b3b..42aeebe847 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -15,9 +15,6 @@ #include <shlobj.h> #include <shlwapi.h> -struct IBindCtx; -struct _SHSTOCKICONINFO; - QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQpaWindows) diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index d851964b9d..e6d451f3da 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -239,7 +239,7 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialo // Create dialog and apply common settings. Check "executed" flag as well // since for example IFileDialog::Show() works only once. if (m_nativeDialog.isNull() || m_nativeDialog->executed()) - m_nativeDialog = QWindowsNativeDialogBasePtr(createNativeDialog()); + m_nativeDialog = QWindowsNativeDialogBasePtr(createNativeDialog(), &QObject::deleteLater); return m_nativeDialog.data(); } @@ -324,12 +324,13 @@ void QWindowsDialogHelperBase<BaseClass>::stopTimer() } } - template <class BaseClass> void QWindowsDialogHelperBase<BaseClass>::hide() { - if (m_nativeDialog) + if (m_nativeDialog) { m_nativeDialog->close(); + m_nativeDialog.clear(); + } m_ownerWindow = nullptr; } @@ -544,8 +545,18 @@ QWindowsShellItem::QWindowsShellItem(IShellItem *item) : m_item(item) , m_attributes(0) { - if (FAILED(item->GetAttributes(SFGAO_CAPABILITYMASK | SFGAO_DISPLAYATTRMASK | SFGAO_CONTENTSMASK | SFGAO_STORAGECAPMASK, &m_attributes))) + SFGAOF mask = (SFGAO_CAPABILITYMASK | SFGAO_CONTENTSMASK | SFGAO_STORAGECAPMASK); + + // Check for attributes which might be expensive to enumerate for subfolders + if (FAILED(item->GetAttributes((SFGAO_STREAM | SFGAO_COMPRESSED), &m_attributes))) { m_attributes = 0; + } else { + // If the item is compressed or stream, skip expensive subfolder test + if (m_attributes & (SFGAO_STREAM | SFGAO_COMPRESSED)) + mask &= ~SFGAO_HASSUBFOLDER; + if (FAILED(item->GetAttributes(mask, &m_attributes))) + m_attributes = 0; + } } QString QWindowsShellItem::path() const @@ -1355,7 +1366,7 @@ Q_GLOBAL_STATIC(QStringList, temporaryItemCopies) static void cleanupTemporaryItemCopies() { - for (const QString &file : qAsConst(*temporaryItemCopies())) + for (const QString &file : std::as_const(*temporaryItemCopies())) QFile::remove(file); } diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 33b1cf2abd..48e0bba41f 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -644,6 +644,85 @@ IDropTargetHelper* QWindowsDrag::dropHelper() { return m_cachedDropTargetHelper; } +// Workaround for DoDragDrop() not working with touch/pen input, causing DnD to hang until the mouse is moved. +// We process pointer messages for touch/pen and generate mouse input through SendInput() to trigger DoDragDrop() +static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect) +{ + QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse(); + const HWND hwnd = underMouse ? reinterpret_cast<HWND>(underMouse->winId()) : ::GetFocus(); + bool starting = false; + + for (;;) { + MSG msg{}; + if (::GetMessage(&msg, hwnd, 0, 0) > 0) { + + if (msg.message == WM_MOUSEMOVE) { + + // Only consider the first simulated event, or actual mouse messages. + if (!starting && (msg.wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON | MK_XBUTTON1 | MK_XBUTTON2)) == 0) + return E_FAIL; + + return ::DoDragDrop(pDataObj, pDropSource, dwOKEffects, pdwEffect); + } + + if (msg.message == WM_POINTERUPDATE) { + + const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); + + POINTER_INFO pointerInfo{}; + if (!GetPointerInfo(pointerId, &pointerInfo)) + return E_FAIL; + + if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) { + + DWORD flags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MOVE; + if (IS_POINTER_FIRSTBUTTON_WPARAM(msg.wParam)) + flags |= MOUSEEVENTF_LEFTDOWN; + if (IS_POINTER_SECONDBUTTON_WPARAM(msg.wParam)) + flags |= MOUSEEVENTF_RIGHTDOWN; + if (IS_POINTER_THIRDBUTTON_WPARAM(msg.wParam)) + flags |= MOUSEEVENTF_MIDDLEDOWN; + + if (!starting) { + POINT pt{}; + if (::GetCursorPos(&pt)) { + + // Send mouse input that can generate a WM_MOUSEMOVE message. + if ((flags & MOUSEEVENTF_LEFTDOWN || flags & MOUSEEVENTF_RIGHTDOWN || flags & MOUSEEVENTF_MIDDLEDOWN) + && (pt.x != pointerInfo.ptPixelLocation.x || pt.y != pointerInfo.ptPixelLocation.y)) { + + const int origin_x = ::GetSystemMetrics(SM_XVIRTUALSCREEN); + const int origin_y = ::GetSystemMetrics(SM_YVIRTUALSCREEN); + const int virt_w = ::GetSystemMetrics(SM_CXVIRTUALSCREEN); + const int virt_h = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); + const int virt_x = pointerInfo.ptPixelLocation.x - origin_x; + const int virt_y = pointerInfo.ptPixelLocation.y - origin_y; + + INPUT input{}; + input.type = INPUT_MOUSE; + input.mi.dx = static_cast<DWORD>(virt_x * (65535.0 / virt_w)); + input.mi.dy = static_cast<DWORD>(virt_y * (65535.0 / virt_h)); + input.mi.dwFlags = flags; + + ::SendInput(1, &input, sizeof(input)); + starting = true; + } + } + } + } + } else { + // Handle other messages. + qWindowsWndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + + if (msg.message == WM_POINTERLEAVE) + return E_FAIL; + } + } else { + return E_FAIL; + } + } +} + Qt::DropAction QWindowsDrag::drag(QDrag *drag) { // TODO: Accessibility handling? @@ -661,7 +740,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) << Qt::hex << int(possibleActions) << "effects=0x" << allowedEffects << Qt::dec; // Indicate message handlers we are in DoDragDrop() event loop. QWindowsDrag::m_dragging = true; - const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); + const HRESULT r = startDoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); QWindowsDrag::m_dragging = false; const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect(); if (r == DRAGDROP_S_DROP) { diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index 65740d69da..cfd73519fa 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -544,11 +544,14 @@ static int choosePixelFormat(HDC hdc, iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; iAttributes[i++] = 1; } + // must be the one before the last one const int samples = format.samples(); const bool sampleBuffersRequested = samples > 1 && testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers); + int sampleBuffersKeyPosition = 0; int samplesValuePosition = 0; if (sampleBuffersRequested) { + sampleBuffersKeyPosition = i; iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; iAttributes[i++] = TRUE; iAttributes[i++] = WGL_SAMPLES_ARB; @@ -560,9 +563,9 @@ static int choosePixelFormat(HDC hdc, } // must be the last bool srgbRequested = format.colorSpace() == QColorSpace::SRgb; - int srgbValuePosition = 0; + int srgbCapableKeyPosition = 0; if (srgbRequested) { - srgbValuePosition = i; + srgbCapableKeyPosition = i; iAttributes[i++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT; iAttributes[i++] = TRUE; } @@ -576,8 +579,9 @@ static int choosePixelFormat(HDC hdc, && numFormats >= 1; if (valid || (!sampleBuffersRequested && !srgbRequested)) break; + // NB reductions must be done in reverse order (disable the last first, then move on to the one before that, etc.) if (srgbRequested) { - iAttributes[srgbValuePosition] = 0; + iAttributes[srgbCapableKeyPosition] = 0; srgbRequested = false; } else if (sampleBuffersRequested) { if (iAttributes[samplesValuePosition] > 1) { @@ -585,11 +589,8 @@ static int choosePixelFormat(HDC hdc, } else if (iAttributes[samplesValuePosition] == 1) { // Fallback in case it is unable to initialize with any // samples to avoid falling back to the GDI path - // NB: The sample attributes needs to be at the end for this - // to work correctly - iAttributes[samplesValuePosition - 1] = FALSE; + iAttributes[sampleBuffersKeyPosition] = 0; iAttributes[samplesValuePosition] = 0; - iAttributes[samplesValuePosition + 1] = 0; } else { break; } diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 9e284f2366..9af99abfad 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -223,6 +223,7 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents); else m_context.initTablet(); + QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication. if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) { @@ -328,10 +329,12 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons requested.geometry = window->isTopLevel() ? QHighDpi::toNativePixels(window->geometry(), window) : QHighDpi::toNativeLocalPosition(window->geometry(), window); - // Apply custom margins (see QWindowsWindow::setCustomMargins())). - const QVariant customMarginsV = window->property("_q_windowsCustomMargins"); - if (customMarginsV.isValid()) - requested.customMargins = qvariant_cast<QMargins>(customMarginsV); + if (!(requested.flags & Qt::FramelessWindowHint)) { + // Apply custom margins (see QWindowsWindow::setCustomMargins())). + const QVariant customMarginsV = window->property("_q_windowsCustomMargins"); + if (customMarginsV.isValid()) + requested.customMargins = qvariant_cast<QMargins>(customMarginsV); + } QWindowsWindowData obtained = QWindowsWindowData::create(window, requested, diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 006396d92a..aa2da267ca 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -15,6 +15,7 @@ #include <QtGui/qevent.h> #include <QtGui/private/qwindowsguieventdispatcher_p.h> #include <QtCore/private/qdebug_p.h> +#include <QtCore/private/qtools_p.h> #if defined(WM_APPCOMMAND) # ifndef FAPPCOMMAND_MOUSE @@ -599,8 +600,7 @@ static inline quint32 toKeyOrUnicode(quint32 vk, quint32 scancode, unsigned char static inline int asciiToKeycode(char a, int state) { - if (a >= 'a' && a <= 'z') - a = toupper(a); + a = QtMiscUtils::toAsciiUpper(a); if ((state & Qt::ControlModifier) != 0) { if (a >= 0 && a <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_ a += '@'; // to @..A..Z.._ @@ -1388,7 +1388,7 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const // Shift+9 over Alt + Shift + 9) resulting in more missing modifiers. if (it == result.end()) result << matchedKey; - else if (missingMods > (*it & Qt::KeyboardModifierMask)) + else if (missingMods > Qt::KeyboardModifiers(*it & Qt::KeyboardModifierMask)) *it = matchedKey; } } diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index 8233e82d8e..9f22da17f0 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -446,7 +446,7 @@ public: bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const { int cf = getCf(formatetc); - return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText(); + return (cf == CF_UNICODETEXT || (cf == CF_TEXT && GetACP() != CP_UTF8)) && mimeData->hasText(); } /* @@ -547,7 +547,8 @@ QList<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QList<FORMATETC> formatics; if (mimeType.startsWith(u"text/plain") && mimeData->hasText()) { formatics += setCf(CF_UNICODETEXT); - formatics += setCf(CF_TEXT); + if (GetACP() != CP_UTF8) + formatics += setCf(CF_TEXT); } return formatics; } @@ -906,6 +907,7 @@ public: QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override; QString mimeForFormat(const FORMATETC &formatetc) const override; private: + bool hasOriginalDIBV5(IDataObject *pDataObj) const; UINT CF_PNG; }; @@ -987,15 +989,41 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeD return false; } +bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const +{ + bool isSynthesized = true; + IEnumFORMATETC *pEnum = nullptr; + HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum); + if (res == S_OK && pEnum) { + FORMATETC fc; + while ((res = pEnum->Next(1, &fc, nullptr)) == S_OK) { + if (fc.ptd) + CoTaskMemFree(fc.ptd); + if (fc.cfFormat == CF_DIB) + break; + if (fc.cfFormat == CF_DIBV5) { + isSynthesized = false; + break; + } + } + pEnum->Release(); + } + return !isSynthesized; +} + QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const { Q_UNUSED(preferredType); QVariant result; if (mimeType != u"application/x-qt-image") return result; - //Try to convert from DIBV5 as it is the most - //widespread format that support transparency - if (canGetData(CF_DIBV5, pDataObj)) { + // Try to convert from DIBV5 as it is the most widespread format that supports transparency, + // but avoid synthesizing it, as that typically loses transparency, e.g. from Office + const bool canGetDibV5 = canGetData(CF_DIBV5, pDataObj); + const bool hasOrigDibV5 = canGetDibV5 ? hasOriginalDIBV5(pDataObj) : false; + qCDebug(lcQpaMime) << "canGetDibV5:" << canGetDibV5 << "hasOrigDibV5:" << hasOrigDibV5; + if (hasOrigDibV5) { + qCDebug(lcQpaMime) << "Decoding DIBV5"; QImage img; QByteArray data = getData(CF_DIBV5, pDataObj); QBuffer buffer(&data); @@ -1004,6 +1032,7 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject * } //PNG, MS Office place this (undocumented) if (canGetData(CF_PNG, pDataObj)) { + qCDebug(lcQpaMime) << "Decoding PNG"; QImage img; QByteArray data = getData(CF_PNG, pDataObj); if (img.loadFromData(data, "PNG")) { @@ -1012,6 +1041,7 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject * } //Fallback to DIB if (canGetData(CF_DIB, pDataObj)) { + qCDebug(lcQpaMime) << "Decoding DIB"; QImage img; QByteArray data = getData(CF_DIBV5, pDataObj); QBuffer buffer(&data); diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index 943eaa2b0a..6f8817cbff 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -524,7 +524,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, // Some devices send touches for each finger in a different message/frame, instead of consolidating // them in the same frame as we were expecting. We account for missing unreleased touches here. - for (auto tp : qAsConst(m_lastTouchPoints)) { + for (auto tp : std::as_const(m_lastTouchPoints)) { if (!inputIds.contains(tp.id)) { tp.state = QEventPoint::State::Stationary; allStates |= tp.state; @@ -798,8 +798,8 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, break; case QT_PT_PEN: #if QT_CONFIG(tabletevent) - if (!m_activeTabletDevice.isNull()) - device = m_activeTabletDevice.data(); + qCDebug(lcQpaTablet) << "ignoring synth-mouse event for tablet event from" << device; + return false; #endif break; } diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 6f0b949b67..f15a7a870d 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -182,11 +182,8 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only // virtual desktop screens. data->flags |= QWindowsScreenData::VirtualDesktop; - if (info.dwFlags & MONITORINFOF_PRIMARY) { + if (info.dwFlags & MONITORINFOF_PRIMARY) data->flags |= QWindowsScreenData::PrimaryScreen; - if ((data->flags & QWindowsScreenData::LockScreen) == 0) - QWindowsFontDatabase::setDefaultVerticalDPI(data->dpi.second); - } return true; } diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index fbad934a05..f2bd90f4bd 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -14,6 +14,7 @@ #include <QtCore/private/qwinregistry_p.h> #include <shlobj.h> +#include <shlwapi.h> #include <intshcut.h> QT_BEGIN_NAMESPACE @@ -25,12 +26,17 @@ enum { debug = 0 }; class QWindowsShellExecuteThread : public QThread { public: - explicit QWindowsShellExecuteThread(const wchar_t *path) : m_path(path) { } + explicit QWindowsShellExecuteThread(const wchar_t *operation, const wchar_t *file, + const wchar_t *parameters) + : m_operation(operation) + , m_file(file) + , m_parameters(parameters) { } void run() override { if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) { - m_result = ShellExecute(nullptr, nullptr, m_path, nullptr, nullptr, SW_SHOWNORMAL); + m_result = ShellExecute(nullptr, m_operation, m_file, m_parameters, nullptr, + SW_SHOWNORMAL); CoUninitialize(); } } @@ -39,9 +45,55 @@ public: private: HINSTANCE m_result = nullptr; - const wchar_t *m_path; + const wchar_t *m_operation; + const wchar_t *m_file; + const wchar_t *m_parameters; }; +static QString msgShellExecuteFailed(const QUrl &url, quintptr code) +{ + QString result; + QTextStream(&result) <<"ShellExecute '" << url.toString() << "' failed (error " << code << ")."; + return result; +} + +// Retrieve the web browser and open the URL. This should be used for URLs with +// fragments which don't work when using ShellExecute() directly (QTBUG-14460, +// QTBUG-55300). +static bool openWebBrowser(const QUrl &url) +{ + WCHAR browserExecutable[MAX_PATH] = {}; + const wchar_t operation[] = L"open"; + DWORD browserExecutableSize = MAX_PATH; + if (FAILED(AssocQueryString(0, ASSOCSTR_EXECUTABLE, L"http", operation, + browserExecutable, &browserExecutableSize))) { + return false; + } + QString browser = QString::fromWCharArray(browserExecutable, browserExecutableSize - 1); + // Workaround for "old" MS Edge entries. Instead of LaunchWinApp.exe we can just use msedge.exe + if (browser.contains("LaunchWinApp.exe"_L1, Qt::CaseInsensitive)) + browser = "msedge.exe"_L1; + const QString urlS = url.toString(QUrl::FullyEncoded); + + // Run ShellExecute() in a thread since it may spin the event loop. + // Prevent it from interfering with processing of posted events (QTBUG-85676). + QWindowsShellExecuteThread thread(operation, + reinterpret_cast<const wchar_t *>(browser.utf16()), + reinterpret_cast<const wchar_t *>(urlS.utf16())); + thread.start(); + thread.wait(); + + const auto result = reinterpret_cast<quintptr>(thread.result()); + if (debug) + qDebug() << __FUNCTION__ << urlS << QString::fromWCharArray(browserExecutable) << result; + // ShellExecute returns a value greater than 32 if successful + if (result <= 32) { + qWarning("%s", qPrintable(msgShellExecuteFailed(url, result))); + return false; + } + return true; +} + static inline bool shellExecute(const QUrl &url) { const QString nativeFilePath = url.isLocalFile() && !url.hasFragment() && !url.hasQuery() @@ -51,7 +103,9 @@ static inline bool shellExecute(const QUrl &url) // Run ShellExecute() in a thread since it may spin the event loop. // Prevent it from interfering with processing of posted events (QTBUG-85676). - QWindowsShellExecuteThread thread(reinterpret_cast<const wchar_t *>(nativeFilePath.utf16())); + QWindowsShellExecuteThread thread(nullptr, + reinterpret_cast<const wchar_t *>(nativeFilePath.utf16()), + nullptr); thread.start(); thread.wait(); @@ -59,7 +113,7 @@ static inline bool shellExecute(const QUrl &url) // ShellExecute returns a value greater than 32 if successful if (result <= 32) { - qWarning("ShellExecute '%ls' failed (error %zu).", qUtf16Printable(url.toString()), result); + qWarning("%s", qPrintable(msgShellExecuteFailed(url, result))); return false; } return true; @@ -136,7 +190,8 @@ bool QWindowsServices::openUrl(const QUrl &url) const QString scheme = url.scheme(); if (scheme == u"mailto" && launchMail(url)) return true; - return shellExecute(url); + return url.isLocalFile() && url.hasFragment() + ? openWebBrowser(url) : shellExecute(url); } bool QWindowsServices::openDocument(const QUrl &url) diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp index cf150b5772..0b04019d89 100644 --- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp +++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp @@ -171,6 +171,7 @@ void QWindowsSystemTrayIcon::updateIcon(const QIcon &icon) qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << icon << ')' << this; if (icon.cacheKey() == m_icon.cacheKey()) return; + m_icon = icon; const HICON hIconToDestroy = createIcon(icon); if (ensureInstalled()) sendTrayMessage(NIM_MODIFY); @@ -409,8 +410,15 @@ bool QWindowsSystemTrayIcon::winEvent(const MSG &message, long *result) QWindowsPopupMenu::notifyTriggered(LOWORD(message.wParam)); break; default: - if (message.message == MYWM_TASKBARCREATED) // self-registered message id (tray crashed) + if (message.message == MYWM_TASKBARCREATED) { + // self-registered message id to handle that + // - screen resolution/DPR changed + const QIcon oldIcon = m_icon; + m_icon = QIcon(); // updateIcon is a no-op if the icon doesn't change + updateIcon(oldIcon); + // - or tray crashed sendTrayMessage(NIM_ADD); + } break; } return false; diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index ef7caa60d9..f622cf7b05 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -26,7 +26,6 @@ #include <QtCore/qvariant.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qdebug.h> -#include <QtCore/qtextstream.h> #include <QtCore/qsysinfo.h> #include <QtCore/qcache.h> #include <QtCore/qthread.h> @@ -47,14 +46,8 @@ #include <algorithm> #if QT_CONFIG(cpp_winrt) && !defined(Q_CC_CLANG) -# include <winrt/base.h> -// Workaround for Windows SDK bug. -// See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47 -namespace winrt::impl -{ - template <typename Async> - auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout); -} +# include <QtCore/private/qt_winrtbase_p.h> + # include <winrt/Windows.UI.ViewManagement.h> # define HAS_UISETTINGS 1 #endif @@ -72,17 +65,6 @@ static inline QColor COLORREFToQColor(COLORREF cr) return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); } -static inline QTextStream& operator<<(QTextStream &str, const QColor &c) -{ - str.setIntegerBase(16); - str.setFieldWidth(2); - str.setPadChar(u'0'); - str << " rgb: #" << c.red() << c.green() << c.blue(); - str.setIntegerBase(10); - str.setFieldWidth(0); - return str; -} - static inline bool booleanSystemParametersInfo(UINT what, bool defaultValue) { BOOL result; @@ -655,20 +637,22 @@ void QWindowsTheme::refreshFonts() clearFonts(); if (!QGuiApplication::desktopSettingsAware()) return; + + const int dpi = 96; NONCLIENTMETRICS ncm; - auto &screenManager = QWindowsContext::instance()->screenManager(); - QWindowsContext::nonClientMetricsForScreen(&ncm, screenManager.screens().value(0)); + QWindowsContext::nonClientMetrics(&ncm, dpi); qCDebug(lcQpaWindows) << __FUNCTION__ << ncm; - const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont); - const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont); - const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont); - const QFont titleFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfCaptionFont); + const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont, dpi); + const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont, dpi); + const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont, dpi); + const QFont titleFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfCaptionFont, dpi); + QFont fixedFont(QStringLiteral("Courier New"), messageBoxFont.pointSize()); fixedFont.setStyleHint(QFont::TypeWriter); LOGFONT lfIconTitleFont; - SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0); - const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont); + SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi); + const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi); m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont()); m_fonts[MenuFont] = new QFont(menuFont); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index a795328bdf..d1a017b6cb 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -33,6 +33,7 @@ #include <private/qguiapplication_p.h> #include <private/qhighdpiscaling_p.h> #include <qpa/qwindowsysteminterface.h> +#include <qpa/qplatformtheme.h> #include <QtCore/qdebug.h> #include <QtCore/qlibraryinfo.h> @@ -850,8 +851,9 @@ static inline bool shouldApplyDarkFrame(const QWindow *w) { if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint)) return false; - if (QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) - return true; + // the application has explicitly opted out of dark frames + if (!QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)) + return false; // if the application supports a dark border, and the palette is dark (window background color // is darker than the text), then turn dark-border support on, otherwise use a light border. const QPalette defaultPalette; @@ -887,7 +889,8 @@ QWindowsWindowData style, exStyle)); QWindowsContext::instance()->setWindowCreationContext(context); - const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME)); + const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME)) + && !(result.flags & Qt::FramelessWindowHint); QMargins invMargins = topLevel && hasFrame && QWindowsGeometryHint::positionIncludesFrame(w) ? invisibleMargins(QPoint(context->frameX, context->frameY)) : QMargins(); @@ -926,11 +929,8 @@ QWindowsWindowData return result; } - if (QWindowsContext::isDarkMode() - && QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames) - && shouldApplyDarkFrame(w)) { - QWindowsWindow::setDarkBorderToWindow(result.hwnd, true); - } + QWindowsWindow::setDarkBorderToWindow(result.hwnd, QWindowsContext::isDarkMode() + && shouldApplyDarkFrame(w)); if (mirrorParentWidth != 0) { context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width() @@ -1408,13 +1408,16 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QScreen * requestedGeometry(geometry), obtainedPos(geometryIn.topLeft()), obtainedSize(geometryIn.size()), - margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)), - customMargins(cm) + margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)) { // Geometry of toplevels does not consider window frames. // TODO: No concept of WA_wasMoved yet that would indicate a // CW_USEDEFAULT unless set. For now, assume that 0,0 means 'default' // for toplevels. + + if (!(w->flags() & Qt::FramelessWindowHint)) + customMargins = cm; + if (geometry.isValid() || !qt_window_private(const_cast<QWindow *>(w))->resizeAutomatic) { frameX = geometry.x(); @@ -1969,7 +1972,15 @@ void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT * const qreal scale = QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) / QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi); const QMargins margins = QWindowsGeometryHint::frame(window(), style(), exStyle(), dpi); - const QSize windowSize = (geometry().size() * scale).grownBy(margins); + if (!(m_data.flags & Qt::FramelessWindowHint)) { + // We need to update the custom margins to match the current DPI, because + // we don't want our users manually hook into this message just to set a + // new margin, but here we can't call setCustomMargins() directly, that + // function will change the window geometry which conflicts with what we + // are currently doing. + m_data.customMargins *= scale; + } + const QSize windowSize = (geometry().size() * scale).grownBy(margins + customMargins()); SIZE *size = reinterpret_cast<SIZE *>(lParam); size->cx = windowSize.width(); size->cy = windowSize.height(); @@ -2119,7 +2130,7 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) { const auto warning = msgUnableToSetGeometry(this, rectIn, m_data.geometry, - m_data.fullFrameMargins, m_data.customMargins); + fullFrameMargins(), customMargins()); qWarning("%s: %s", __FUNCTION__, qPrintable(warning)); } } else { @@ -2303,9 +2314,22 @@ static inline bool isSoftwareGl() } bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, - WPARAM, LPARAM, LRESULT *result) + WPARAM wParam, LPARAM, LRESULT *result) { if (message == WM_ERASEBKGND) { // Backing store - ignored. + if (!m_firstBgDraw && QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) { + // Get system background color + const QColor bgColor = QGuiApplicationPrivate::platformTheme()->palette()->color(QPalette::Window); + HBRUSH bgBrush = CreateSolidBrush(RGB(bgColor.red(), bgColor.green(), bgColor.blue())); + // Fill rectangle with system background color + RECT rc; + auto hdc = reinterpret_cast<HDC>(wParam); + GetClientRect(hwnd, &rc); + FillRect(hdc, &rc, bgBrush); + DeleteObject(bgBrush); + // Brush the window with system background color only for first time + m_firstBgDraw = true; + } *result = 1; return true; } @@ -2375,7 +2399,8 @@ QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt, QWindowsWindowData result = m_data; result.flags = creationData.flags; result.embedded = creationData.embedded; - result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME)); + result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME)) + && !(creationData.flags & Qt::FramelessWindowHint); return result; } @@ -2594,6 +2619,9 @@ void QWindowsWindow::setExStyle(unsigned s) const bool QWindowsWindow::windowEvent(QEvent *event) { switch (event->type()) { + case QEvent::ApplicationPaletteChange: + setDarkBorder(QWindowsContext::isDarkMode()); + break; case QEvent::WindowBlocked: // Blocked by another modal window. setEnabled(false); setFlag(BlockedByModal); @@ -2662,6 +2690,8 @@ bool QWindowsWindow::handleGeometryChanging(MSG *message) const void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins) { + if (m_data.flags & Qt::FramelessWindowHint) + return; if (m_data.fullFrameMargins != newMargins) { qCDebug(lcQpaWindows) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins; m_data.fullFrameMargins = newMargins; @@ -2679,12 +2709,14 @@ void QWindowsWindow::updateFullFrameMargins() void QWindowsWindow::calculateFullFrameMargins() { + if (m_data.flags & Qt::FramelessWindowHint) + return; // Normally obtained from WM_NCCALCSIZE. This calculation only works // when no native menu is present. const auto systemMargins = testFlag(DisableNonClientScaling) ? QWindowsGeometryHint::frameOnPrimaryScreen(window(), m_data.hwnd) : frameMargins_sys(); - setFullFrameMargins(systemMargins + m_data.customMargins); + setFullFrameMargins(systemMargins + customMargins()); } QMargins QWindowsWindow::frameMargins() const @@ -2697,6 +2729,8 @@ QMargins QWindowsWindow::frameMargins() const QMargins QWindowsWindow::fullFrameMargins() const { + if (m_data.flags & Qt::FramelessWindowHint) + return {}; return m_data.fullFrameMargins; } @@ -3165,8 +3199,12 @@ bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d) void QWindowsWindow::setDarkBorder(bool d) { - if (shouldApplyDarkFrame(window()) && queryDarkBorder(m_data.hwnd) != d) - setDarkBorderToWindow(m_data.hwnd, d); + // respect explicit opt-out and incompatible palettes or styles + d = d && shouldApplyDarkFrame(window()); + if (queryDarkBorder(m_data.hwnd) == d) + return; + + setDarkBorderToWindow(m_data.hwnd, d); } QWindowsMenuBar *QWindowsWindow::menuBar() const @@ -3179,6 +3217,13 @@ void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb) m_menuBar = mb; } +QMargins QWindowsWindow::customMargins() const +{ + if (m_data.flags & Qt::FramelessWindowHint) + return {}; + return m_data.customMargins; +} + /*! \brief Sets custom margins to be added to the default margins determined by the windows style in the handling of the WM_NCCALCSIZE message. @@ -3191,6 +3236,10 @@ void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb) void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) { + if (m_data.flags & Qt::FramelessWindowHint) { + qCWarning(lcQpaWindows) << "You should not set custom margins for a frameless window."; + return; + } if (newCustomMargins != m_data.customMargins) { const QMargins oldCustomMargins = m_data.customMargins; m_data.customMargins = newCustomMargins; diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 29dfbdb856..49e80522b4 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -272,7 +272,7 @@ public: QWindowsMenuBar *menuBar() const; void setMenuBar(QWindowsMenuBar *mb); - QMargins customMargins() const override { return m_data.customMargins; } + QMargins customMargins() const override; void setCustomMargins(const QMargins &m) override; void setStyle(unsigned s) const; @@ -377,6 +377,7 @@ private: HICON m_iconBig = nullptr; void *m_surface = nullptr; int m_savedDpi = 96; + bool m_firstBgDraw = false; static bool m_screenForGLInitialized; diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index 2d5d91a7ea..16f0a3f9a6 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -38,10 +38,13 @@ QT_BEGIN_NAMESPACE using namespace QWindowsUiAutomation; +QMutex QWindowsUiaMainProvider::m_mutex; // Returns a cached instance of the provider for a specific accessible interface. QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessibleInterface *accessible) { + QMutexLocker locker(&m_mutex); + if (!accessible) return nullptr; @@ -71,10 +74,11 @@ QWindowsUiaMainProvider::~QWindowsUiaMainProvider() void QWindowsUiaMainProvider::notifyFocusChange(QAccessibleEvent *event) { if (QAccessibleInterface *accessible = event->accessibleInterface()) { - // If this is a table/tree/list, raise event for the focused cell/item instead. - if (accessible->tableInterface()) + // If this is a complex element, raise event for the focused child instead. + if (accessible->childCount()) { if (QAccessibleInterface *child = accessible->focusChild()) accessible = child; + } if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId); } @@ -103,6 +107,10 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { if (accessible->state().active) { QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowOpenedEventId); + if (QAccessibleInterface *focused = accessible->focusChild()) { + if (QWindowsUiaMainProvider *focusedProvider = providerForAccessible(focused)) + QWindowsUiaWrapper::instance()->raiseAutomationEvent(focusedProvider, UIA_AutomationFocusChangedEventId); + } } else { QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowClosedEventId); } @@ -235,6 +243,8 @@ ULONG QWindowsUiaMainProvider::AddRef() ULONG STDMETHODCALLTYPE QWindowsUiaMainProvider::Release() { + QMutexLocker locker(&m_mutex); + if (!--m_ref) { delete this; return 0; @@ -699,26 +709,18 @@ HRESULT QWindowsUiaMainProvider::ElementProviderFromPoint(double x, double y, IR QPoint point; nativeUiaPointToPoint(uiaPoint, window, &point); - if (auto targetacc = accessible->childAt(point.x(), point.y())) { - auto acc = accessible->childAt(point.x(), point.y()); - // Reject the cases where childAt() returns a different instance in each call for the same - // element (e.g., QAccessibleTree), as it causes an endless loop with Youdao Dictionary installed. - if (targetacc == acc) { - // Controls can be embedded within grouping elements. By default returns the innermost control. - while (acc) { - targetacc = acc; - // For accessibility tools it may be better to return the text element instead of its subcomponents. - if (targetacc->textInterface()) break; - acc = targetacc->childAt(point.x(), point.y()); - if (acc != targetacc->childAt(point.x(), point.y())) { - qCDebug(lcQpaUiAutomation) << "Non-unique childAt() for" << targetacc; - break; - } - } - *pRetVal = providerForAccessible(targetacc); - } else { - qCDebug(lcQpaUiAutomation) << "Non-unique childAt() for" << accessible; + QAccessibleInterface *targetacc = accessible->childAt(point.x(), point.y()); + + if (targetacc) { + QAccessibleInterface *acc = targetacc; + // Controls can be embedded within grouping elements. By default returns the innermost control. + while (acc) { + targetacc = acc; + // For accessibility tools it may be better to return the text element instead of its subcomponents. + if (targetacc->textInterface()) break; + acc = acc->childAt(point.x(), point.y()); } + *pRetVal = providerForAccessible(targetacc); } return S_OK; } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h index 5659a28e35..fb5069f620 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h @@ -11,6 +11,7 @@ #include <QtCore/qpointer.h> #include <QtCore/qsharedpointer.h> +#include <QtCore/qmutex.h> #include <QtCore/qt_windows.h> #include <QtGui/qaccessible.h> @@ -62,6 +63,7 @@ public: private: QString automationIdForAccessible(const QAccessibleInterface *accessible); ULONG m_ref; + static QMutex m_mutex; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp index c53b130b92..ba2a88bb4e 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp @@ -39,7 +39,7 @@ void QWindowsUiaProviderCache::insert(QAccessible::Id id, QWindowsUiaBaseProvide m_providerTable[id] = provider; m_inverseTable[provider] = id; // Connects the destroyed signal to our slot, to remove deleted objects from the cache. - QObject::connect(provider, &QObject::destroyed, this, &QWindowsUiaProviderCache::objectDestroyed); + QObject::connect(provider, &QObject::destroyed, this, &QWindowsUiaProviderCache::objectDestroyed, Qt::DirectConnection); } } |