diff options
Diffstat (limited to 'src/plugins/platforms/windows')
114 files changed, 4003 insertions, 6309 deletions
diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt index 880fa79ef5..4b92317978 100644 --- a/src/plugins/platforms/windows/CMakeLists.txt +++ b/src/plugins/platforms/windows/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from windows.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## QWindowsIntegrationPlugin Plugin: @@ -7,26 +8,26 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin OUTPUT_NAME qwindows PLUGIN_TYPE platforms - DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES windows # special case + DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES windows SOURCES main.cpp qtwindowsglobal.h qwin10helpers.cpp qwin10helpers.h qwindowsapplication.cpp qwindowsapplication.h qwindowsbackingstore.cpp qwindowsbackingstore.h - qwindowscombase.h qwindowscontext.cpp qwindowscontext.h qwindowscursor.cpp qwindowscursor.h qwindowsdialoghelpers.cpp qwindowsdialoghelpers.h qwindowsdropdataobject.cpp qwindowsdropdataobject.h qwindowsgdiintegration.cpp qwindowsgdiintegration.h qwindowsgdinativeinterface.cpp qwindowsgdinativeinterface.h + qwindowsiconengine.cpp qwindowsiconengine.h qwindowsinputcontext.cpp qwindowsinputcontext.h qwindowsintegration.cpp qwindowsintegration.h qwindowsinternalmimedata.cpp qwindowsinternalmimedata.h qwindowskeymapper.cpp qwindowskeymapper.h qwindowsmenu.cpp qwindowsmenu.h - qwindowsmime.cpp qwindowsmime.h + qwindowsmimeregistry.cpp qwindowsmimeregistry.h qwindowsmousehandler.cpp qwindowsmousehandler.h qwindowsnativeinterface.cpp qwindowsnativeinterface.h qwindowsole.cpp qwindowsole.h @@ -37,6 +38,8 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin qwindowstheme.cpp qwindowstheme.h qwindowsthreadpoolrunner.h qwindowswindow.cpp qwindowswindow.h + NO_UNITY_BUILD_SOURCES + qwindowspointerhandler.cpp DEFINES QT_NO_CAST_FROM_ASCII QT_NO_FOREACH @@ -53,12 +56,17 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin imm32 ole32 oleaut32 + setupapi shell32 shlwapi user32 winmm winspool wtsapi32 + shcore + comdlg32 + d3d9 + runtimeobject ) # Resources: @@ -76,10 +84,6 @@ qt_internal_add_resource(QWindowsIntegrationPlugin "openglblacklists" ${openglblacklists_resource_files} ) - -#### Keys ignored in scope 1:.:.:windows.pro:<TRUE>: -# OTHER_FILES = "windows.json" - ## Scopes: ##################################################################### @@ -91,9 +95,6 @@ qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_opengl Qt::OpenGLPrivate ) -#### Keys ignored in scope 3:.:.:windows.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: -# PLUGIN_EXTENDS = "-" - qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_opengl AND NOT QT_FEATURE_dynamicgl LIBRARIES opengl32 @@ -102,6 +103,8 @@ qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_opengl qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION MINGW LIBRARIES uuid + NO_PCH_SOURCES + qwindowspointerhandler.cpp ) qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_systemtrayicon @@ -172,6 +175,7 @@ endif() qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_accessibility SOURCES + uiautomation/qwindowsuiautomation.cpp uiautomation/qwindowsuiautomation.h uiautomation/qwindowsuiaaccessibility.cpp uiautomation/qwindowsuiaaccessibility.h uiautomation/qwindowsuiabaseprovider.cpp uiautomation/qwindowsuiabaseprovider.h uiautomation/qwindowsuiaexpandcollapseprovider.cpp uiautomation/qwindowsuiaexpandcollapseprovider.h @@ -193,13 +197,17 @@ qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_accessi uiautomation/qwindowsuiawindowprovider.cpp uiautomation/qwindowsuiawindowprovider.h ) +if(QT_FEATURE_accessibility) + find_library(UI_AUTOMATION_LIBRARY uiautomationcore) + if(UI_AUTOMATION_LIBRARY) + qt_internal_extend_target(QWindowsIntegrationPlugin + LIBRARIES + ${UI_AUTOMATION_LIBRARY} + ) + endif() +endif() + qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION MINGW AND QT_FEATURE_accessibility LIBRARIES uuid ) - -# begin special case -if (MINGW) - set_source_files_properties(qwindowspointerhandler.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON) -endif() -# end special case diff --git a/src/plugins/platforms/windows/cursors.qrc b/src/plugins/platforms/windows/cursors.qrc deleted file mode 100644 index fded527aac..0000000000 --- a/src/plugins/platforms/windows/cursors.qrc +++ /dev/null @@ -1,25 +0,0 @@ -<RCC> - <qresource prefix="/qt-project.org/windows/cursors"> - <file>images/closedhandcursor_32.png</file> - <file>images/closedhandcursor_48.png</file> - <file>images/closedhandcursor_64.png</file> - <file>images/dragcopycursor_32.png</file> - <file>images/dragcopycursor_48.png</file> - <file>images/dragcopycursor_64.png</file> - <file>images/draglinkcursor_32.png</file> - <file>images/draglinkcursor_48.png</file> - <file>images/draglinkcursor_64.png</file> - <file>images/dragmovecursor_32.png</file> - <file>images/dragmovecursor_48.png</file> - <file>images/dragmovecursor_64.png</file> - <file>images/openhandcursor_32.png</file> - <file>images/openhandcursor_48.png</file> - <file>images/openhandcursor_64.png</file> - <file>images/splithcursor_32.png</file> - <file>images/splithcursor_48.png</file> - <file>images/splithcursor_64.png</file> - <file>images/splitvcursor_32.png</file> - <file>images/splitvcursor_48.png</file> - <file>images/splitvcursor_64.png</file> - </qresource> -</RCC> diff --git a/src/plugins/platforms/windows/main.cpp b/src/plugins/platforms/windows/main.cpp index a3f81ec9fc..51c1fb4a45 100644 --- a/src/plugins/platforms/windows/main.cpp +++ b/src/plugins/platforms/windows/main.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qpa/qplatformintegrationplugin.h> @@ -45,6 +9,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + /*! \title Qt platform plugin for Windows @@ -106,7 +72,7 @@ public: QPlatformIntegration *QWindowsIntegrationPlugin::create(const QString& system, const QStringList& paramList, int &, char **) { - if (system.compare(system, QLatin1String("windows"), Qt::CaseInsensitive) == 0) + if (system.compare(system, "windows"_L1, Qt::CaseInsensitive) == 0) return new QWindowsGdiIntegration(paramList); return nullptr; } diff --git a/src/plugins/platforms/windows/openglblacklists.qrc b/src/plugins/platforms/windows/openglblacklists.qrc deleted file mode 100644 index 9f0c186c21..0000000000 --- a/src/plugins/platforms/windows/openglblacklists.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<RCC> - <qresource prefix="/qt-project.org/windows/openglblacklists"> - <file alias="default.json">openglblacklists/default.json</file> - </qresource> -</RCC> diff --git a/src/plugins/platforms/windows/openglblacklists/default.json b/src/plugins/platforms/windows/openglblacklists/default.json index e37351f9e0..072acdd115 100644 --- a/src/plugins/platforms/windows/openglblacklists/default.json +++ b/src/plugins/platforms/windows/openglblacklists/default.json @@ -93,7 +93,7 @@ }, { "id": 8, - "description": "Standard VGA: Insufficent support for OpenGL, D3D9 and D3D11", + "description": "Standard VGA: Insufficient support for OpenGL, D3D9 and D3D11", "vendor_id": "0x0000", "device_id": ["0x0000"], "os": { diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h index 573a8d07c8..96a72600eb 100644 --- a/src/plugins/platforms/windows/qtwindowsglobal.h +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QTWINDOWSGLOBAL_H #define QTWINDOWSGLOBAL_H @@ -44,8 +8,16 @@ #include <QtCore/qt_windows.h> #include <QtCore/qnamespace.h> -#ifndef WM_DWMCOMPOSITIONCHANGED // MinGW. -# define WM_DWMCOMPOSITIONCHANGED 0x31E +#ifndef WM_DWMCOMPOSITIONCHANGED +# define WM_DWMCOMPOSITIONCHANGED 0x31E +#endif + +#ifndef WM_DWMCOLORIZATIONCOLORCHANGED +# define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 +#endif + +#ifndef WM_SYSCOLORCHANGE +# define WM_SYSCOLORCHANGE 0x0015 #endif #ifndef WM_TOUCH @@ -60,6 +32,10 @@ # define WM_DPICHANGED 0x02E0 #endif +#ifndef WM_GETDPISCALEDSIZE +# define WM_GETDPISCALEDSIZE 0x02E4 +#endif + // WM_POINTER support from Windows 8 onwards (WINVER >= 0x0602) #ifndef WM_POINTERUPDATE # define WM_NCPOINTERUPDATE 0x0241 @@ -89,7 +65,7 @@ QT_BEGIN_NAMESPACE namespace QtWindows { -enum +enum WindowsEventTypeFlags { WindowEventFlag = 0x10000, MouseEventFlag = 0x20000, @@ -129,6 +105,9 @@ enum WindowsEventType // Simplify event types EnterSizeMoveEvent = WindowEventFlag + 22, ExitSizeMoveEvent = WindowEventFlag + 23, PointerActivateWindowEvent = WindowEventFlag + 24, + DpiScaledSizeEvent = WindowEventFlag + 25, + DpiChangedAfterParentEvent = WindowEventFlag + 27, + TaskbarButtonCreated = WindowEventFlag + 28, MouseEvent = MouseEventFlag + 1, MouseWheelEvent = MouseEventFlag + 2, CursorEvent = MouseEventFlag + 3, @@ -140,7 +119,7 @@ enum WindowsEventType // Simplify event types NonClientPointerEvent = NonClientEventFlag + PointerEventFlag + 4, KeyEvent = KeyEventFlag + 1, KeyDownEvent = KeyEventFlag + KeyDownEventFlag + 1, - KeyboardLayoutChangeEvent = KeyEventFlag + 2, + InputLanguageChangeEvent = KeyEventFlag + 2, InputMethodKeyEvent = InputMethodEventFlag + KeyEventFlag + 1, InputMethodKeyDownEvent = InputMethodEventFlag + KeyEventFlag + KeyDownEventFlag + 1, ClipboardEvent = ClipboardEventFlag + 1, @@ -162,27 +141,37 @@ enum WindowsEventType // Simplify event types InputMethodRequest = InputMethodEventFlag + 6, ThemeChanged = ThemingEventFlag + 1, CompositionSettingsChanged = ThemingEventFlag + 2, - DisplayChangedEvent = 437, - SettingChangedEvent = DisplayChangedEvent + 1, + SettingChangedEvent = 438, ScrollEvent = GenericEventFlag + 1, ContextMenu = 123, 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 +enum class DpiAwareness { - ProcessDpiUnaware, - ProcessSystemDpiAware, - ProcessPerMonitorDpiAware, - ProcessPerMonitorV2DpiAware // Qt extension (not in Process_DPI_Awareness) + Invalid = -1, + Unaware, + System, + PerMonitor, + PerMonitorVersion2, + Unaware_GdiScaled }; } // namespace QtWindows inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamIn, LPARAM lParamIn) { + static const UINT WM_TASKBAR_BUTTON_CREATED = []{ + UINT message = RegisterWindowMessage(L"TaskbarButtonCreated"); + // In case the application is run elevated, allow the + // TaskbarButtonCreated message through. + ChangeWindowMessageFilter(message, MSGFLT_ADD); + return message; + }(); + switch (message) { case WM_PAINT: case WM_ERASEBKGND: @@ -241,7 +230,7 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI return QtWindows::InputMethodKeyDownEvent; #ifdef WM_INPUTLANGCHANGE case WM_INPUTLANGCHANGE: - return QtWindows::KeyboardLayoutChangeEvent; + return QtWindows::InputLanguageChangeEvent; #endif // WM_INPUTLANGCHANGE case WM_TOUCH: return QtWindows::TouchEvent; @@ -280,12 +269,9 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI // http://msdn.microsoft.com/en-us/library/ms695534(v=vs.85).aspx case WM_SETTINGCHANGE: return QtWindows::SettingChangedEvent; - case WM_DISPLAYCHANGE: - return QtWindows::DisplayChangedEvent; case WM_THEMECHANGED: -#ifdef WM_SYSCOLORCHANGE // Windows 7: Handle color change as theme change (QTBUG-34170). - case WM_SYSCOLORCHANGE: -#endif + case WM_SYSCOLORCHANGE: // Handle color change as theme change (QTBUG-34170). + case WM_DWMCOLORIZATIONCOLORCHANGED: return QtWindows::ThemeChanged; case WM_DWMCOMPOSITIONCHANGED: return QtWindows::CompositionSettingsChanged; @@ -316,6 +302,10 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI return HIWORD(wParamIn) ? QtWindows::AcceleratorCommandEvent : QtWindows::MenuCommandEvent; case WM_DPICHANGED: return QtWindows::DpiChangedEvent; + case WM_DPICHANGED_AFTERPARENT: + return QtWindows::DpiChangedAfterParentEvent; + case WM_GETDPISCALEDSIZE: + return QtWindows::DpiScaledSizeEvent; case WM_ENTERSIZEMOVE: return QtWindows::EnterSizeMoveEvent; case WM_EXITSIZEMOVE: @@ -332,9 +322,15 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI return QtWindows::NonClientPointerEvent; if (message >= WM_POINTERUPDATE && message <= WM_POINTERHWHEEL) return QtWindows::PointerEvent; + if (message == WM_TASKBAR_BUTTON_CREATED) + return QtWindows::TaskbarButtonCreated; return QtWindows::UnknownEvent; } +#ifndef QT_NO_DEBUG_STREAM +extern QDebug operator<<(QDebug, QtWindows::DpiAwareness); +#endif + QT_END_NAMESPACE #endif // QTWINDOWSGLOBAL_H diff --git a/src/plugins/platforms/windows/qwin10helpers.cpp b/src/plugins/platforms/windows/qwin10helpers.cpp index 9a7fce9cd5..026e81cb0c 100644 --- a/src/plugins/platforms/windows/qwin10helpers.cpp +++ b/src/plugins/platforms/windows/qwin10helpers.cpp @@ -1,52 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwin10helpers.h" #include <QtCore/qdebug.h> -#include <QtCore/qoperatingsystemversion.h> -#include <QtCore/private/qsystemlibrary_p.h> +#include <winstring.h> +#include <roapi.h> #if defined(Q_CC_MINGW) || defined(Q_CC_CLANG) # define HAS_UI_VIEW_SETTINGS_INTEROP // Present from MSVC2015 + SDK 10 onwards -#elif (!defined(Q_CC_MSVC) || _MSC_VER >= 1900) && NTDDI_VERSION >= 0xa000000 +#elif (!defined(Q_CC_MSVC) || _MSC_VER >= 1900) && WINVER >= 0x0A00 # define HAS_UI_VIEW_SETTINGS_INTEROP # define HAS_UI_VIEW_SETTINGS #endif @@ -96,56 +60,23 @@ public: QT_BEGIN_NAMESPACE -// Starting from Windows 10 -struct QWindowsComBaseDLL -{ - bool init(); - bool isValid() const - { - return roGetActivationFactory != nullptr && windowsCreateStringReference != nullptr; - } - - typedef HRESULT (WINAPI *RoGetActivationFactory)(HSTRING, REFIID, void **); - typedef HRESULT (WINAPI *WindowsCreateStringReference)(PCWSTR, UINT32, HSTRING_HEADER *, HSTRING *); - - RoGetActivationFactory roGetActivationFactory = nullptr; - WindowsCreateStringReference windowsCreateStringReference = nullptr; -}; - -static QWindowsComBaseDLL baseComDll; - -bool QWindowsComBaseDLL::init() -{ - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 && !isValid()) { - QSystemLibrary library(QStringLiteral("combase")); - roGetActivationFactory = - reinterpret_cast<RoGetActivationFactory>(library.resolve("RoGetActivationFactory")); - windowsCreateStringReference = - reinterpret_cast<WindowsCreateStringReference>(library.resolve("WindowsCreateStringReference")); - } - return isValid(); -} - // Return tablet mode, note: Does not work for GetDesktopWindow(). bool qt_windowsIsTabletMode(HWND hwnd) { bool result = false; - if (!baseComDll.init()) - return false; - const wchar_t uiViewSettingsId[] = L"Windows.UI.ViewManagement.UIViewSettings"; HSTRING_HEADER uiViewSettingsIdRefHeader; HSTRING uiViewSettingsIdHs = nullptr; const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1); - if (FAILED(baseComDll.windowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs))) + if (FAILED(WindowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs))) return false; IUIViewSettingsInterop *uiViewSettingsInterop = nullptr; // __uuidof(IUIViewSettingsInterop); const GUID uiViewSettingsInteropRefId = {0x3694dbf9, 0x8f68, 0x44be,{0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6}}; - HRESULT hr = baseComDll.roGetActivationFactory(uiViewSettingsIdHs, uiViewSettingsInteropRefId, + HRESULT hr = RoGetActivationFactory(uiViewSettingsIdHs, uiViewSettingsInteropRefId, reinterpret_cast<void **>(&uiViewSettingsInterop)); if (FAILED(hr)) return false; diff --git a/src/plugins/platforms/windows/qwin10helpers.h b/src/plugins/platforms/windows/qwin10helpers.h index 4f364dfc59..e601947267 100644 --- a/src/plugins/platforms/windows/qwin10helpers.h +++ b/src/plugins/platforms/windows/qwin10helpers.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWIN10HELPERS_H #define QWIN10HELPERS_H diff --git a/src/plugins/platforms/windows/qwindowsapplication.cpp b/src/plugins/platforms/windows/qwindowsapplication.cpp index 9997e0a903..42e34ac99f 100644 --- a/src/plugins/platforms/windows/qwindowsapplication.cpp +++ b/src/plugins/platforms/windows/qwindowsapplication.cpp @@ -1,50 +1,20 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsapplication.h" #include "qwindowsclipboard.h" #include "qwindowscontext.h" -#include "qwindowsmime.h" +#include "qwindowsmimeregistry.h" #include "qwin10helpers.h" #include "qwindowsopengltester.h" +#include "qwindowswindow.h" +#include "qwindowsintegration.h" +#include "qwindowstheme.h" -#include <QtCore/QVariant> +#include <QtCore/qvariant.h> +#include <QtCore/private/qfunctions_win_p.h> + +#include <QtGui/qpalette.h> QT_BEGIN_NAMESPACE @@ -72,6 +42,11 @@ void QWindowsApplication::setWindowActivationBehavior(WindowActivationBehavior b m_windowActivationBehavior = behavior; } +void QWindowsApplication::setHasBorderInFullScreenDefault(bool border) +{ + QWindowsWindow::setHasBorderInFullScreenDefault(border); +} + bool QWindowsApplication::isTabletMode() const { #if QT_CONFIG(clipboard) @@ -97,11 +72,6 @@ bool QWindowsApplication::setWinTabEnabled(bool enabled) return enabled ? ctx->initTablet() : ctx->disposeTablet(); } -bool QWindowsApplication::isDarkMode() const -{ - return QWindowsContext::isDarkMode(); -} - QWindowsApplication::DarkModeHandling QWindowsApplication::darkModeHandling() const { return m_darkModeHandling; @@ -112,13 +82,13 @@ void QWindowsApplication::setDarkModeHandling(QWindowsApplication::DarkModeHandl m_darkModeHandling = handling; } -void QWindowsApplication::registerMime(QNativeInterface::Private::QWindowsMime *mime) +void QWindowsApplication::registerMime(QWindowsMimeConverter *mime) { if (auto ctx = QWindowsContext::instance()) ctx->mimeConverter().registerMime(mime); } -void QWindowsApplication::unregisterMime(QNativeInterface::Private::QWindowsMime *mime) +void QWindowsApplication::unregisterMime(QWindowsMimeConverter *mime) { if (auto ctx = QWindowsContext::instance()) ctx->mimeConverter().unregisterMime(mime); @@ -126,7 +96,7 @@ void QWindowsApplication::unregisterMime(QNativeInterface::Private::QWindowsMime int QWindowsApplication::registerMimeType(const QString &mime) { - return QWindowsMimeConverter::registerMimeType(mime); + return QWindowsMimeRegistry::registerMimeType(mime); } HWND QWindowsApplication::createMessageWindow(const QString &classNameTemplate, @@ -168,4 +138,9 @@ QVariant QWindowsApplication::gpuList() const return result; } +void QWindowsApplication::populateLightSystemPalette(QPalette &result) const +{ + result = QWindowsTheme::systemPalette(Qt::ColorScheme::Light); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsapplication.h b/src/plugins/platforms/windows/qwindowsapplication.h index d57ce263c9..0918df91af 100644 --- a/src/plugins/platforms/windows/qwindowsapplication.h +++ b/src/plugins/platforms/windows/qwindowsapplication.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSAPPLICATION_H #define QWINDOWSAPPLICATION_H @@ -53,17 +17,18 @@ public: WindowActivationBehavior windowActivationBehavior() const override; void setWindowActivationBehavior(WindowActivationBehavior behavior) override; + void setHasBorderInFullScreenDefault(bool border) override; + bool isTabletMode() const override; bool isWinTabEnabled() const override; bool setWinTabEnabled(bool enabled) override; - bool isDarkMode() const override; DarkModeHandling darkModeHandling() const override; void setDarkModeHandling(DarkModeHandling handling) override; - void registerMime(QNativeInterface::Private::QWindowsMime *mime) override; - void unregisterMime(QNativeInterface::Private::QWindowsMime *mime) override; + void registerMime(QWindowsMimeConverter *mime) override; + void unregisterMime(QWindowsMimeConverter *mime) override; int registerMimeType(const QString &mime) override; @@ -77,6 +42,8 @@ public: QVariant gpu() const override; QVariant gpuList() const override; + void populateLightSystemPalette(QPalette &palette) const override; + private: WindowActivationBehavior m_windowActivationBehavior = DefaultActivateWindow; TouchWindowTouchTypes m_touchWindowTouchTypes = NormalTouch; diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp index e42237db13..07918f6094 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp +++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsbackingstore.h" #include "qwindowswindow.h" @@ -153,7 +117,7 @@ void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) if (QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha) m_alphaNeedsFill = true; else // upgrade but here we know app painting does not rely on alpha hence no need to fill - format = qt_maybeAlphaVersionWithSameDepth(format); + format = qt_maybeDataCompatibleAlphaVersion(format); QWindowsNativeImage *oldwni = m_image.data(); auto *newwni = new QWindowsNativeImage(size.width(), size.height(), format); @@ -182,8 +146,8 @@ bool QWindowsBackingStore::scroll(const QRegion &area, int dx, int dy) return false; const QPoint offset(dx, dy); - for (const QRect &rect : area) - qt_scrollRectInImage(m_image->image(), rect, offset); + const QRect rect = area.boundingRect(); + qt_scrollRectInImage(m_image->image(), rect, offset); return true; } diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.h b/src/plugins/platforms/windows/qwindowsbackingstore.h index b96c8f0e61..186123b38e 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.h +++ b/src/plugins/platforms/windows/qwindowsbackingstore.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSBACKINGSTORE_H #define QWINDOWSBACKINGSTORE_H diff --git a/src/plugins/platforms/windows/qwindowsclipboard.cpp b/src/plugins/platforms/windows/qwindowsclipboard.cpp index 01377a55e0..7a6d41e0b3 100644 --- a/src/plugins/platforms/windows/qwindowsclipboard.cpp +++ b/src/plugins/platforms/windows/qwindowsclipboard.cpp @@ -1,46 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsclipboard.h" #include "qwindowscontext.h" #include "qwindowsole.h" -#include "qwindowsmime.h" #include <QtGui/qguiapplication.h> #include <QtGui/qclipboard.h> @@ -53,6 +16,7 @@ #include <QtCore/qthread.h> #include <QtCore/qvariant.h> #include <QtCore/qurl.h> +#include <QtCore/private/qsystemerror_p.h> #include <QtGui/private/qwindowsguieventdispatcher_p.h> @@ -191,12 +155,9 @@ void QWindowsClipboard::registerViewer() createDummyWindow(QStringLiteral("ClipboardView"), L"QtClipboardView", qClipboardViewerWndProc, WS_OVERLAPPED); - // Try format listener API (Vista onwards) first. - if (QWindowsContext::user32dll.addClipboardFormatListener && QWindowsContext::user32dll.removeClipboardFormatListener) { - m_formatListenerRegistered = QWindowsContext::user32dll.addClipboardFormatListener(m_clipboardViewer); - if (!m_formatListenerRegistered) - qErrnoWarning("AddClipboardFormatListener() failed."); - } + m_formatListenerRegistered = AddClipboardFormatListener(m_clipboardViewer); + if (!m_formatListenerRegistered) + qErrnoWarning("AddClipboardFormatListener() failed."); if (!m_formatListenerRegistered) m_nextClipboardViewer = SetClipboardViewer(m_clipboardViewer); @@ -210,7 +171,7 @@ void QWindowsClipboard::unregisterViewer() { if (m_clipboardViewer) { if (m_formatListenerRegistered) { - QWindowsContext::user32dll.removeClipboardFormatListener(m_clipboardViewer); + RemoveClipboardFormatListener(m_clipboardViewer); m_formatListenerRegistered = false; } else { ChangeClipboardChain(m_clipboardViewer, m_nextClipboardViewer); @@ -340,7 +301,7 @@ void QWindowsClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) mimeData->formats().join(u", ") : QString(QStringLiteral("NULL")); qErrnoWarning("OleSetClipboard: Failed to set mime data (%s) on clipboard: %s", qPrintable(mimeDataFormats), - QWindowsContext::comErrorString(src).constData()); + qPrintable(QSystemError::windowsComString(src))); releaseIData(); return; } diff --git a/src/plugins/platforms/windows/qwindowsclipboard.h b/src/plugins/platforms/windows/qwindowsclipboard.h index 24a6bc908d..9713dccf9b 100644 --- a/src/plugins/platforms/windows/qwindowsclipboard.h +++ b/src/plugins/platforms/windows/qwindowsclipboard.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSCLIPBOARD_H #define QWINDOWSCLIPBOARD_H diff --git a/src/plugins/platforms/windows/qwindowscombase.h b/src/plugins/platforms/windows/qwindowscombase.h deleted file mode 100644 index 749ad50234..0000000000 --- a/src/plugins/platforms/windows/qwindowscombase.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWINDOWSCOMBASE_H -#define QWINDOWSCOMBASE_H - -#include <QtCore/qglobal.h> - -#include <unknwn.h> - -QT_BEGIN_NAMESPACE - -// The __uuidof operator of MinGW does not work for all interfaces (for example, -// IAccessible2). Specializations of this function can be provides to work -// around this. -template <class DesiredInterface> -static IID qUuidOf() { return __uuidof(DesiredInterface); } - -// Helper for implementing IUnknown::QueryInterface. -template <class DesiredInterface, class Derived> -bool qWindowsComQueryInterface(Derived *d, REFIID id, LPVOID *iface) -{ - if (id == qUuidOf<DesiredInterface>()) { - *iface = static_cast<DesiredInterface *>(d); - d->AddRef(); - return true; - } - return false; -} - -// Helper for implementing IUnknown::QueryInterface for IUnknown -// in the case of multiple inheritance via the first inherited class. -template <class FirstInheritedInterface, class Derived> -bool qWindowsComQueryUnknownInterfaceMulti(Derived *d, REFIID id, LPVOID *iface) -{ - if (id == __uuidof(IUnknown)) { - *iface = static_cast<FirstInheritedInterface *>(d); - d->AddRef(); - return true; - } - return false; -} - -// Helper base class to provide IUnknown methods for COM classes (single inheritance) -template <class ComInterface> class QWindowsComBase : public ComInterface -{ - Q_DISABLE_COPY_MOVE(QWindowsComBase) -public: - explicit QWindowsComBase(ULONG initialRefCount = 1) : m_ref(initialRefCount) {} - virtual ~QWindowsComBase() = default; - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override - { - *iface = nullptr; - return qWindowsComQueryInterface<IUnknown>(this, id, iface) || qWindowsComQueryInterface<ComInterface>(this, id, iface) - ? S_OK : E_NOINTERFACE; - } - - ULONG STDMETHODCALLTYPE AddRef() override { return ++m_ref; } - - ULONG STDMETHODCALLTYPE Release() override - { - if (!--m_ref) { - delete this; - return 0; - } - return m_ref; - } - -private: - ULONG m_ref; -}; - -// Clang does not consider __declspec(nothrow) as nothrow -QT_WARNING_DISABLE_CLANG("-Wmicrosoft-exception-spec") -QT_WARNING_DISABLE_CLANG("-Wmissing-exception-spec") - -QT_END_NAMESPACE - -#endif // QWINDOWSCOMBASE_H diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 433e58a97a..de65a2171d 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowscontext.h" #include "qwindowsintegration.h" @@ -47,7 +11,7 @@ #include "qwindowspointerhandler.h" #include "qtwindowsglobal.h" #include "qwindowsmenu.h" -#include "qwindowsmime.h" +#include "qwindowsmimeregistry.h" #include "qwindowsinputcontext.h" #if QT_CONFIG(tabletevent) # include "qwindowstabletsupport.h" @@ -77,25 +41,30 @@ #include <QtCore/qlibraryinfo.h> #include <QtCore/qstringlist.h> #include <QtCore/qdebug.h> -#include <QtCore/qoperatingsystemversion.h> #include <QtCore/qsysinfo.h> #include <QtCore/qscopedpointer.h> #include <QtCore/quuid.h> -#include <QtCore/private/qsystemlibrary_p.h> #include <QtCore/private/qwinregistry_p.h> +#if QT_CONFIG(cpp_winrt) +# include <QtCore/private/qfactorycacheregistration_p.h> +#endif +#include <QtCore/private/qsystemerror_p.h> #include <QtGui/private/qwindowsguieventdispatcher_p.h> +#include <QtGui/private/qwindowsthemecache_p.h> #include <stdlib.h> #include <stdio.h> #include <windowsx.h> -#include <comdef.h> #include <dbt.h> #include <wtsapi32.h> +#include <shellscalingapi.h> QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcQpaWindows, "qt.qpa.windows") +using namespace Qt::StringLiterals; + +Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window") Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl") Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") @@ -106,6 +75,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility") Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation") Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon") +Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") int QWindowsContext::verbose = 0; @@ -151,95 +121,6 @@ static inline bool sessionManagerInteractionBlocked() static inline bool sessionManagerInteractionBlocked() { return false; } #endif -static inline int windowDpiAwareness(HWND hwnd) -{ - return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext - ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext(QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd)) - : -1; -} - -// Note: This only works within WM_NCCREATE -static bool enableNonClientDpiScaling(HWND hwnd) -{ - bool result = false; - if (QWindowsContext::user32dll.enableNonClientDpiScaling && windowDpiAwareness(hwnd) == 2) { - result = QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE; - if (!result) { - const DWORD errorCode = GetLastError(); - qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)", - hwnd, errorCode); - } - } - return result; -} - -/*! - \class QWindowsUser32DLL - \brief Struct that contains dynamically resolved symbols of User32.dll. - - The stub libraries shipped with the MinGW compiler miss some of the - functions. They need to be retrieved dynamically. - - In addition, touch-related functions are available only from Windows onwards. - These need to resolved dynamically for Q_CC_MSVC as well. - - \sa QWindowsShell32DLL - - \internal -*/ - -void QWindowsUser32DLL::init() -{ - QSystemLibrary library(QStringLiteral("user32")); - setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware"); - setProcessDpiAwarenessContext = (SetProcessDpiAwarenessContext)library.resolve("SetProcessDpiAwarenessContext"); - - addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener"); - removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener"); - - getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences"); - setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences"); - - enableMouseInPointer = (EnableMouseInPointer)library.resolve("EnableMouseInPointer"); - getPointerType = (GetPointerType)library.resolve("GetPointerType"); - getPointerInfo = (GetPointerInfo)library.resolve("GetPointerInfo"); - getPointerDeviceRects = (GetPointerDeviceRects)library.resolve("GetPointerDeviceRects"); - getPointerTouchInfo = (GetPointerTouchInfo)library.resolve("GetPointerTouchInfo"); - getPointerFrameTouchInfo = (GetPointerFrameTouchInfo)library.resolve("GetPointerFrameTouchInfo"); - getPointerFrameTouchInfoHistory = (GetPointerFrameTouchInfoHistory)library.resolve("GetPointerFrameTouchInfoHistory"); - getPointerPenInfo = (GetPointerPenInfo)library.resolve("GetPointerPenInfo"); - getPointerPenInfoHistory = (GetPointerPenInfoHistory)library.resolve("GetPointerPenInfoHistory"); - skipPointerFrameMessages = (SkipPointerFrameMessages)library.resolve("SkipPointerFrameMessages"); - - if (QOperatingSystemVersion::current() - >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) { - adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi"); - enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling"); - getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext"); - getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext"); - systemParametersInfoForDpi = (SystemParametersInfoForDpi)library.resolve("SystemParametersInfoForDpi"); - getDpiForWindow = (GetDpiForWindow)library.resolve("GetDpiForWindow"); - } -} - -bool QWindowsUser32DLL::supportsPointerApi() -{ - return enableMouseInPointer && getPointerType && getPointerInfo && getPointerDeviceRects - && getPointerTouchInfo && getPointerFrameTouchInfo && getPointerFrameTouchInfoHistory - && getPointerPenInfo && getPointerPenInfoHistory && skipPointerFrameMessages; -} - -void QWindowsShcoreDLL::init() -{ - QSystemLibrary library(QStringLiteral("SHCore")); - getProcessDpiAwareness = (GetProcessDpiAwareness)library.resolve("GetProcessDpiAwareness"); - setProcessDpiAwareness = (SetProcessDpiAwareness)library.resolve("SetProcessDpiAwareness"); - getDpiForMonitor = (GetDpiForMonitor)library.resolve("GetDpiForMonitor"); -} - -QWindowsUser32DLL QWindowsContext::user32dll; -QWindowsShcoreDLL QWindowsContext::shcoredll; - QWindowsContext *QWindowsContext::m_instance = nullptr; /*! @@ -251,20 +132,18 @@ QWindowsContext *QWindowsContext::m_instance = nullptr; \internal */ -typedef QHash<HWND, QWindowsWindow *> HandleBaseWindowHash; - struct QWindowsContextPrivate { QWindowsContextPrivate(); unsigned m_systemInfo = 0; QSet<QString> m_registeredWindowClassNames; - HandleBaseWindowHash m_windows; + QWindowsContext::HandleBaseWindowHash m_windows; HDC m_displayContext = nullptr; int m_defaultDPI = 96; QWindowsKeyMapper m_keyMapper; QWindowsMouseHandler m_mouseHandler; QWindowsPointerHandler m_pointerHandler; - QWindowsMimeConverter m_mimeConverter; + QWindowsMimeRegistry m_mimeConverter; QWindowsScreenManager m_screenManager; QSharedPointer<QWindowCreationContext> m_creationContext; #if QT_CONFIG(tabletevent) @@ -275,19 +154,14 @@ struct QWindowsContextPrivate { bool m_asyncExpose = false; HPOWERNOTIFY m_powerNotification = nullptr; HWND m_powerDummyWindow = nullptr; - static bool m_darkMode; static bool m_v2DpiAware; }; -bool QWindowsContextPrivate::m_darkMode = false; bool QWindowsContextPrivate::m_v2DpiAware = false; QWindowsContextPrivate::QWindowsContextPrivate() : m_oleInitializeResult(OleInitialize(nullptr)) { - QWindowsContext::user32dll.init(); - QWindowsContext::shcoredll.init(); - if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice()) m_systemInfo |= QWindowsContext::SI_SupportsTouch; m_displayContext = GetDC(nullptr); @@ -296,10 +170,9 @@ QWindowsContextPrivate::QWindowsContextPrivate() m_systemInfo |= QWindowsContext::SI_RTL_Extensions; m_keyMapper.setUseRTLExtensions(true); } - m_darkMode = QWindowsTheme::queryDarkMode(); if (FAILED(m_oleInitializeResult)) { qWarning() << "QWindowsContext: OleInitialize() failed: " - << QWindowsContext::comErrorString(m_oleInitializeResult); + << QSystemError::windowsComString(m_oleInitializeResult); } } @@ -329,8 +202,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) @@ -372,7 +249,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(); } } @@ -402,9 +279,6 @@ bool QWindowsContext::initPointer(unsigned integrationOptions) if (integrationOptions & QWindowsIntegration::DontUseWMPointer) return false; - if (!QWindowsContext::user32dll.supportsPointerApi()) - return false; - d->m_systemInfo |= QWindowsContext::SI_SupportsPointer; return true; } @@ -472,56 +346,119 @@ void QWindowsContext::setDetectAltGrModifier(bool a) d->m_keyMapper.setDetectAltGrModifier(a); } -int QWindowsContext::processDpiAwareness() -{ - int result; - if (QWindowsContext::shcoredll.getProcessDpiAwareness - && SUCCEEDED(QWindowsContext::shcoredll.getProcessDpiAwareness(nullptr, &result))) { - return result; +[[nodiscard]] static inline QtWindows::DpiAwareness + dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT context) +{ + // IsValidDpiAwarenessContext() will handle the NULL pointer case. + if (!IsValidDpiAwarenessContext(context)) + return QtWindows::DpiAwareness::Invalid; + if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) + return QtWindows::DpiAwareness::Unaware_GdiScaled; + if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + return QtWindows::DpiAwareness::PerMonitorVersion2; + if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) + return QtWindows::DpiAwareness::PerMonitor; + if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) + return QtWindows::DpiAwareness::System; + if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE)) + return QtWindows::DpiAwareness::Unaware; + return QtWindows::DpiAwareness::Invalid; +} + +QtWindows::DpiAwareness QWindowsContext::windowDpiAwareness(HWND hwnd) +{ + if (!hwnd) + return QtWindows::DpiAwareness::Invalid; + const auto context = GetWindowDpiAwarenessContext(hwnd); + return dpiAwarenessContextToQtDpiAwareness(context); +} + +QtWindows::DpiAwareness QWindowsContext::processDpiAwareness() +{ + // Although we have GetDpiAwarenessContextForProcess(), however, + // it's only available on Win10 1903+, which is a little higher + // than Qt's minimum supported version (1809), so we can't use it. + // Luckily, MS docs said GetThreadDpiAwarenessContext() will also + // return the default DPI_AWARENESS_CONTEXT for the process if + // SetThreadDpiAwarenessContext() was never called. So we can use + // it as an equivalent. + const auto context = GetThreadDpiAwarenessContext(); + return dpiAwarenessContextToQtDpiAwareness(context); +} + +[[nodiscard]] static inline DPI_AWARENESS_CONTEXT + qtDpiAwarenessToDpiAwarenessContext(QtWindows::DpiAwareness dpiAwareness) +{ + switch (dpiAwareness) { + case QtWindows::DpiAwareness::Invalid: + return nullptr; + case QtWindows::DpiAwareness::Unaware: + return DPI_AWARENESS_CONTEXT_UNAWARE; + case QtWindows::DpiAwareness::System: + return DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; + case QtWindows::DpiAwareness::PerMonitor: + return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; + case QtWindows::DpiAwareness::PerMonitorVersion2: + return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + case QtWindows::DpiAwareness::Unaware_GdiScaled: + return DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED; } - return -1; + return nullptr; } -void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness) +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, QtWindows::DpiAwareness dpiAwareness) { - qCDebug(lcQpaWindows) << __FUNCTION__ << dpiAwareness; - if (QWindowsContext::shcoredll.isValid()) { - const HRESULT hr = QWindowsContext::shcoredll.setProcessDpiAwareness(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(" - << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr) - << ", using " << QWindowsContext::processDpiAwareness(); - } - } else { - if (dpiAwareness != QtWindows::ProcessDpiUnaware && QWindowsContext::user32dll.setProcessDPIAware) { - if (!QWindowsContext::user32dll.setProcessDPIAware()) - qErrnoWarning("SetProcessDPIAware() failed"); - } + const QDebugStateSaver saver(d); + QString message = u"QtWindows::DpiAwareness::"_s; + switch (dpiAwareness) { + case QtWindows::DpiAwareness::Invalid: + message += u"Invalid"_s; + break; + case QtWindows::DpiAwareness::Unaware: + message += u"Unaware"_s; + break; + case QtWindows::DpiAwareness::System: + message += u"System"_s; + break; + case QtWindows::DpiAwareness::PerMonitor: + message += u"PerMonitor"_s; + break; + case QtWindows::DpiAwareness::PerMonitorVersion2: + message += u"PerMonitorVersion2"_s; + break; + case QtWindows::DpiAwareness::Unaware_GdiScaled: + message += u"Unaware_GdiScaled"_s; + break; } + d.nospace().noquote() << message; + return d; } +#endif -void QWindowsContext::setProcessDpiV2Awareness() +bool QWindowsContext::setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness) { - qCDebug(lcQpaWindows) << __FUNCTION__; - const BOOL ok = QWindowsContext::user32dll.setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - if (ok) { - QWindowsContextPrivate::m_v2DpiAware = true; - } else { - const HRESULT errorCode = 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 (errorCode != ERROR_ACCESS_DENIED || lcQpaWindows().isDebugEnabled()) { - qWarning().noquote().nospace() << "setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: " - << QWindowsContext::comErrorString(errorCode); - } + qCDebug(lcQpaWindow) << __FUNCTION__ << dpiAwareness; + if (processDpiAwareness() == dpiAwareness) + return true; + const auto context = qtDpiAwarenessToDpiAwarenessContext(dpiAwareness); + if (!IsValidDpiAwarenessContext(context)) { + qCWarning(lcQpaWindow) << dpiAwareness << "is not supported by current system."; + return false; } -} - -bool QWindowsContext::isDarkMode() -{ - return QWindowsContextPrivate::m_darkMode; + if (!SetProcessDpiAwarenessContext(context)) { + qCWarning(lcQpaWindow).noquote().nospace() + << "SetProcessDpiAwarenessContext() failed: " + << QSystemError::windowsString() + << "\nQt's default DPI awareness context is " + << "DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. If you know what you " + << "are doing, you can overwrite this default using qt.conf " + << "(https://doc.qt.io/qt-6/highdpi.html#configuring-windows)."; + return false; + } + QWindowsContextPrivate::m_v2DpiAware + = processDpiAwareness() == QtWindows::DpiAwareness::PerMonitorVersion2; + return true; } QWindowsContext *QWindowsContext::instance() @@ -539,9 +476,14 @@ bool QWindowsContext::useRTLExtensions() const return d->m_keyMapper.useRTLExtensions(); } -QList<int> QWindowsContext::possibleKeys(const QKeyEvent *e) const +QPlatformKeyMapper *QWindowsContext::keyMapper() const { - return d->m_keyMapper.possibleKeys(e); + return &d->m_keyMapper; +} + +QWindowsContext::HandleBaseWindowHash &QWindowsContext::windows() +{ + return d->m_windows; } QSharedPointer<QWindowCreationContext> QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx) @@ -588,6 +530,8 @@ QString QWindowsContext::classNamePrefix() # define xstr(s) str(s) # define str(s) #s str << xstr(QT_NAMESPACE); +# undef str +# undef xstr #endif } return result; @@ -625,30 +569,30 @@ QString QWindowsContext::registerWindowClass(const QWindow *w) } // Create a unique name for the flag combination QString cname = classNamePrefix(); - cname += QLatin1String("QWindow"); + cname += "QWindow"_L1; switch (type) { case Qt::Tool: - cname += QLatin1String("Tool"); + cname += "Tool"_L1; break; case Qt::ToolTip: - cname += QLatin1String("ToolTip"); + cname += "ToolTip"_L1; break; case Qt::Popup: - cname += QLatin1String("Popup"); + cname += "Popup"_L1; break; default: break; } if (style & CS_DROPSHADOW) - cname += QLatin1String("DropShadow"); + cname += "DropShadow"_L1; if (style & CS_SAVEBITS) - cname += QLatin1String("SaveBits"); + cname += "SaveBits"_L1; if (style & CS_OWNDC) - cname += QLatin1String("OwnDC"); + cname += "OwnDC"_L1; if (icon) - cname += QLatin1String("Icon"); + cname += "Icon"_L1; - return registerWindowClass(cname, qWindowsWndProc, style, GetSysColorBrush(COLOR_WINDOW), icon); + return registerWindowClass(cname, qWindowsWndProc, style, nullptr, icon); } QString QWindowsContext::registerWindowClass(QString cname, @@ -707,7 +651,7 @@ QString QWindowsContext::registerWindowClass(QString cname, qPrintable(cname)); d->m_registeredWindowClassNames.insert(cname); - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << cname + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << ' ' << cname << " style=0x" << Qt::hex << style << Qt::dec << " brush=" << brush << " icon=" << icon << " atom=" << atom; return cname; @@ -717,7 +661,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)); } @@ -729,23 +673,6 @@ int QWindowsContext::screenDepth() const return GetDeviceCaps(d->m_displayContext, BITSPIXEL); } -QString QWindowsContext::windowsErrorMessage(unsigned long errorCode) -{ - QString rc = QString::fromLatin1("#%1: ").arg(errorCode); - char16_t *lpMsgBuf; - - const DWORD len = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, errorCode, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, nullptr); - if (len) { - rc = QString::fromUtf16(lpMsgBuf, int(len)); - LocalFree(lpMsgBuf); - } else { - rc += QString::fromLatin1("<unknown error>"); - } - return rc; -} - void QWindowsContext::addWindow(HWND hwnd, QWindowsWindow *w) { d->m_windows.insert(hwnd, w); @@ -849,6 +776,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; @@ -906,7 +835,7 @@ bool QWindowsContext::isSessionLocked() return result; } -QWindowsMimeConverter &QWindowsContext::mimeConverter() const +QWindowsMimeRegistry &QWindowsContext::mimeConverter() const { return d->m_mimeConverter; } @@ -944,98 +873,6 @@ HWND QWindowsContext::createDummyWindow(const QString &classNameIn, HWND_MESSAGE, nullptr, static_cast<HINSTANCE>(GetModuleHandle(nullptr)), nullptr); } -// Re-engineered from the inline function _com_error::ErrorMessage(). -// We cannot use it directly since it uses swprintf_s(), which is not -// present in the MSVCRT.DLL found on Windows XP (QTBUG-35617). -static inline QString errorMessageFromComError(const _com_error &comError) -{ - TCHAR *message = nullptr; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - nullptr, DWORD(comError.Error()), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), - message, 0, nullptr); - if (message) { - const QString result = QString::fromWCharArray(message).trimmed(); - LocalFree(static_cast<HLOCAL>(message)); - return result; - } - if (const WORD wCode = comError.WCode()) - return QString::asprintf("IDispatch error #%u", uint(wCode)); - return QString::asprintf("Unknown error 0x0%x", uint(comError.Error())); -} - -/*! - \brief Common COM error strings. -*/ - -QByteArray QWindowsContext::comErrorString(HRESULT hr) -{ - QByteArray result = QByteArrayLiteral("COM error 0x") - + QByteArray::number(quintptr(hr), 16) + ' '; - switch (hr) { - case S_OK: - result += QByteArrayLiteral("S_OK"); - break; - case S_FALSE: - result += QByteArrayLiteral("S_FALSE"); - break; - case E_UNEXPECTED: - result += QByteArrayLiteral("E_UNEXPECTED"); - break; - case E_ACCESSDENIED: - result += QByteArrayLiteral("E_ACCESSDENIED"); - break; - case CO_E_ALREADYINITIALIZED: - result += QByteArrayLiteral("CO_E_ALREADYINITIALIZED"); - break; - case CO_E_NOTINITIALIZED: - result += QByteArrayLiteral("CO_E_NOTINITIALIZED"); - break; - case RPC_E_CHANGED_MODE: - result += QByteArrayLiteral("RPC_E_CHANGED_MODE"); - break; - case OLE_E_WRONGCOMPOBJ: - result += QByteArrayLiteral("OLE_E_WRONGCOMPOBJ"); - break; - case CO_E_NOT_SUPPORTED: - result += QByteArrayLiteral("CO_E_NOT_SUPPORTED"); - break; - case E_NOTIMPL: - result += QByteArrayLiteral("E_NOTIMPL"); - break; - case E_INVALIDARG: - result += QByteArrayLiteral("E_INVALIDARG"); - break; - case E_NOINTERFACE: - result += QByteArrayLiteral("E_NOINTERFACE"); - break; - case E_POINTER: - result += QByteArrayLiteral("E_POINTER"); - break; - case E_HANDLE: - result += QByteArrayLiteral("E_HANDLE"); - break; - case E_ABORT: - result += QByteArrayLiteral("E_ABORT"); - break; - case E_FAIL: - result += QByteArrayLiteral("E_FAIL"); - break; - case RPC_E_WRONG_THREAD: - result += QByteArrayLiteral("RPC_E_WRONG_THREAD"); - break; - case RPC_E_THREAD_NOT_INIT: - result += QByteArrayLiteral("RPC_E_THREAD_NOT_INIT"); - break; - default: - break; - } - _com_error error(hr); - result += QByteArrayLiteral(" ("); - result += errorMessageFromComError(error).toUtf8(); - result += ')'; - return result; -} - void QWindowsContext::forceNcCalcSize(HWND hwnd) { // Force WM_NCCALCSIZE to adjust margin @@ -1046,8 +883,8 @@ void QWindowsContext::forceNcCalcSize(HWND hwnd) bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi) { - const BOOL result = QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0 - ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi) + const BOOL result = dpi != 0 + ? SystemParametersInfoForDpi(action, param, out, 0, dpi) : SystemParametersInfo(action, param, out, 0); return result == TRUE; } @@ -1094,8 +931,7 @@ bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window) if (QWindowsContextPrivate::m_v2DpiAware) return true; - return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 - && window->isTopLevel() + return window->isTopLevel() && !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid() #if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL && (window->surfaceType() != QSurface::OpenGLSurface @@ -1131,6 +967,21 @@ static inline bool isInputMessage(UINT m) || (m >= WM_KEYFIRST && m <= WM_KEYLAST); } +// Note: This only works within WM_NCCREATE +static bool enableNonClientDpiScaling(HWND hwnd) +{ + bool result = false; + if (QWindowsContext::windowDpiAwareness(hwnd) == QtWindows::DpiAwareness::PerMonitor) { + result = EnableNonClientDpiScaling(hwnd) != FALSE; + if (!result) { + const DWORD errorCode = GetLastError(); + qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)", + hwnd, errorCode); + } + } + return result; +} + /*! \brief Main windows procedure registered for windows. @@ -1147,9 +998,10 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, MSG msg; msg.hwnd = hwnd; // re-create MSG structure - msg.message = message; // time and pt fields ignored + msg.message = message; msg.wParam = wParam; msg.lParam = lParam; + msg.time = GetMessageTime(); msg.pt.x = msg.pt.y = 0; if (et != QtWindows::CursorEvent && (et & (QtWindows::MouseEventFlag | QtWindows::NonClientEventFlag))) { msg.pt.x = GET_X_LPARAM(lParam); @@ -1228,27 +1080,12 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, #else return false; #endif - case QtWindows::DisplayChangedEvent: - if (QWindowsTheme *t = QWindowsTheme::instance()) - t->displayChanged(); - QWindowsWindow::displayChanged(); - d->m_screenManager.handleScreenChanges(); - return false; case QtWindows::SettingChangedEvent: { QWindowsWindow::settingsChanged(); - const bool darkMode = QWindowsTheme::queryDarkMode(); - if (darkMode != QWindowsContextPrivate::m_darkMode) { - QWindowsContextPrivate::m_darkMode = darkMode; - auto integration = QWindowsIntegration::instance(); - if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)) { - for (QWindowsWindow *w : d->m_windows) - w->setDarkBorder(QWindowsContextPrivate::m_darkMode); - } - if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) { - QWindowsTheme::instance()->refresh(); - for (QWindowsWindow *w : d->m_windows) - QWindowSystemInterface::handleThemeChange(w->window()); - } + // Only refresh the window theme if the user changes the personalize settings. + if ((wParam == 0) && (lParam != 0) // lParam sometimes may be NULL. + && (wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0)) { + QWindowsTheme::handleSettingsChanged(); } return d->m_screenManager.handleScreenChanges(); } @@ -1265,7 +1102,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, d->m_creationContext->applyToMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam)); return true; case QtWindows::ResizeEvent: - d->m_creationContext->obtainedSize = QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + d->m_creationContext->obtainedSize = QSize(LOWORD(lParam), HIWORD(lParam)); return true; case QtWindows::MoveEvent: d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); @@ -1308,7 +1145,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, if (wParam == DBT_DEVNODES_CHANGED) initTouch(); break; - case QtWindows::KeyboardLayoutChangeEvent: + case QtWindows::InputLanguageChangeEvent: if (QWindowsInputContext *wic = windowsInputContext()) wic->handleInputLanguageChanged(wParam, lParam); Q_FALLTHROUGH(); @@ -1338,7 +1175,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, platformWindow->handleMoved(); return true; case QtWindows::ResizeEvent: - platformWindow->handleResized(static_cast<int>(wParam)); + platformWindow->handleResized(static_cast<int>(wParam), lParam); return true; case QtWindows::QuerySizeHints: platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam)); @@ -1348,25 +1185,33 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::NonClientHitTest: return platformWindow->handleNonClientHitTest(QPoint(msg.pt.x, msg.pt.y), result); case QtWindows::GeometryChangingEvent: - return platformWindow->QWindowsWindow::handleGeometryChanging(&msg); + return platformWindow->handleGeometryChanging(&msg); case QtWindows::ExposeEvent: - return platformWindow->handleWmPaint(hwnd, message, wParam, lParam); + return platformWindow->handleWmPaint(hwnd, message, wParam, lParam, result); case QtWindows::NonClientMouseEvent: - if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled()) + if (!platformWindow->frameStrutEventsEnabled()) + break; + if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); else return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); case QtWindows::NonClientPointerEvent: - if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled()) + if (!platformWindow->frameStrutEventsEnabled()) + break; + if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result); break; case QtWindows::EnterSizeMoveEvent: platformWindow->setFlag(QWindowsWindow::ResizeMoveActive); + if (!IsZoomed(hwnd)) + platformWindow->updateRestoreGeometry(); return true; case QtWindows::ExitSizeMoveEvent: platformWindow->clearFlag(QWindowsWindow::ResizeMoveActive); platformWindow->checkForScreenChanged(); handleExitSizeMove(platformWindow->window()); + if (!IsZoomed(hwnd)) + platformWindow->updateRestoreGeometry(); return true; case QtWindows::ScrollEvent: if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) @@ -1412,6 +1257,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, QWindowSystemInterface::handleCloseEvent(platformWindow->window()); return true; case QtWindows::ThemeChanged: { + QWindowsThemeCache::clearThemeCache(platformWindow->handle()); // Switch from Aero to Classic changes margins. if (QWindowsTheme *theme = QWindowsTheme::instance()) theme->windowsThemeChanged(platformWindow->window()); @@ -1453,34 +1299,15 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; #endif } break; - case QtWindows::DpiChangedEvent: { - - const UINT dpi = HIWORD(wParam); - const qreal scale = qreal(dpi) / qreal(platformWindow->savedDpi()); - platformWindow->setSavedDpi(dpi); - - // Send screen change first, so that the new sceen is set during any following resize - platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange); - - // Apply the suggested window geometry to the native window. This will make - // sure the window tracks the mouse cursor during screen change, and also - // that the window size is scaled according to the DPI change. - platformWindow->updateFullFrameMargins(); - const auto prcNewWindow = reinterpret_cast<RECT *>(lParam); - SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, - prcNewWindow->right - prcNewWindow->left, - prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); - - // Scale child QPlatformWindow size. Windows sends WM_DPICHANGE to top-level windows only. - for (QWindow *childWindow : platformWindow->window()->findChildren<QWindow *>()) { - QWindowsWindow *platformChildWindow = static_cast<QWindowsWindow *>(childWindow->handle()); - QRect currentGeometry = platformChildWindow->geometry(); - QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale); - platformChildWindow->setGeometry(scaledGeometry); - } - + case QtWindows::DpiScaledSizeEvent: + platformWindow->handleDpiScaledSize(wParam, lParam, result); + return true; + case QtWindows::DpiChangedEvent: + platformWindow->handleDpiChanged(hwnd, wParam, lParam); + return true; + case QtWindows::DpiChangedAfterParentEvent: + platformWindow->handleDpiChangedAfterParent(hwnd); return true; - } #if QT_CONFIG(sessionmanager) case QtWindows::QueryEndSessionApplicationEvent: { QWindowsSessionManager *sessionManager = platformSessionManager(); @@ -1522,6 +1349,11 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; } #endif // !defined(QT_NO_SESSIONMANAGER) + case QtWindows::TaskbarButtonCreated: + // Apply application badge if this is the first time we have a taskbar + // button, or after Explorer restart. + QWindowsIntegration::instance()->updateApplicationBadge(); + break; default: break; } @@ -1564,7 +1396,7 @@ void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et, } if (nextActiveWindow != d->m_lastActiveWindow) { d->m_lastActiveWindow = nextActiveWindow; - QWindowSystemInterface::handleWindowActivated(nextActiveWindow); + QWindowSystemInterface::handleFocusWindowChanged(nextActiveWindow, Qt::ActiveWindowFocusReason); } } @@ -1594,7 +1426,7 @@ bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg) } QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos, - QWindowsKeyMapper::queryKeyboardModifiers()); + keyMapper()->queryKeyboardModifiers()); return true; } #endif @@ -1615,7 +1447,7 @@ void QWindowsContext::handleExitSizeMove(QWindow *window) const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons(); if (currentButtons == appButtons) return; - const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const Qt::KeyboardModifiers keyboardModifiers = keyMapper()->queryKeyboardModifiers(); const QPoint globalPos = QWindowsCursor::mousePosition(); const QPlatformWindow *platWin = window->handle(); const QPoint localPos = platWin->mapFromGlobal(globalPos); @@ -1624,8 +1456,7 @@ void QWindowsContext::handleExitSizeMove(QWindow *window) for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) { if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) { QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, - currentButtons, button, type, - keyboardModifiers); + currentButtons, button, type, keyboardModifiers); } } if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) @@ -1719,7 +1550,7 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0)); if (margins.left() >= 0) { if (platformWindow) { - qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins; + qCDebug(lcQpaWindow) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins; platformWindow->setFullFrameMargins(margins); } else { const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext(); diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 04328a0369..0539a22afc 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSCONTEXT_H #define QWINDOWSCONTEXT_H @@ -51,12 +15,9 @@ #include <shlobj.h> #include <shlwapi.h> -struct IBindCtx; -struct _SHSTOCKICONINFO; - QT_BEGIN_NAMESPACE -Q_DECLARE_LOGGING_CATEGORY(lcQpaWindows) +Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow) Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents) Q_DECLARE_LOGGING_CATEGORY(lcQpaGl) Q_DECLARE_LOGGING_CATEGORY(lcQpaMime) @@ -67,104 +28,27 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaTablet) Q_DECLARE_LOGGING_CATEGORY(lcQpaAccessibility) Q_DECLARE_LOGGING_CATEGORY(lcQpaUiAutomation) Q_DECLARE_LOGGING_CATEGORY(lcQpaTrayIcon) +Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen) class QWindow; class QPlatformScreen; class QPlatformWindow; +class QPlatformKeyMapper; class QWindowsMenuBar; class QWindowsScreenManager; class QWindowsTabletSupport; class QWindowsWindow; -class QWindowsMimeConverter; +class QWindowsMimeRegistry; struct QWindowCreationContext; struct QWindowsContextPrivate; class QPoint; class QKeyEvent; class QPointingDevice; - -struct QWindowsUser32DLL -{ - inline void init(); - inline bool supportsPointerApi(); - - typedef BOOL (WINAPI *EnableMouseInPointer)(BOOL); - typedef BOOL (WINAPI *GetPointerType)(UINT32, PVOID); - typedef BOOL (WINAPI *GetPointerInfo)(UINT32, PVOID); - typedef BOOL (WINAPI *GetPointerDeviceRects)(HANDLE, RECT *, RECT *); - typedef BOOL (WINAPI *GetPointerTouchInfo)(UINT32, PVOID); - typedef BOOL (WINAPI *GetPointerFrameTouchInfo)(UINT32, UINT32 *, PVOID); - typedef BOOL (WINAPI *GetPointerFrameTouchInfoHistory)(UINT32, UINT32 *, UINT32 *, PVOID); - typedef BOOL (WINAPI *GetPointerPenInfo)(UINT32, PVOID); - typedef BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32, UINT32 *, PVOID); - typedef BOOL (WINAPI *SkipPointerFrameMessages)(UINT32); - typedef BOOL (WINAPI *SetProcessDPIAware)(); - typedef BOOL (WINAPI *SetProcessDpiAwarenessContext)(HANDLE); - typedef BOOL (WINAPI *AddClipboardFormatListener)(HWND); - typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND); - typedef BOOL (WINAPI *GetDisplayAutoRotationPreferences)(DWORD *); - typedef BOOL (WINAPI *SetDisplayAutoRotationPreferences)(DWORD); - typedef BOOL (WINAPI *AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); - typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND); - typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND); - typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int); - typedef BOOL (WINAPI *SystemParametersInfoForDpi)(UINT, UINT, PVOID, UINT, UINT); - typedef int (WINAPI *GetDpiForWindow)(HWND); - - // Windows pointer functions (Windows 8 or later). - EnableMouseInPointer enableMouseInPointer = nullptr; - GetPointerType getPointerType = nullptr; - GetPointerInfo getPointerInfo = nullptr; - GetPointerDeviceRects getPointerDeviceRects = nullptr; - GetPointerTouchInfo getPointerTouchInfo = nullptr; - GetPointerFrameTouchInfo getPointerFrameTouchInfo = nullptr; - GetPointerFrameTouchInfoHistory getPointerFrameTouchInfoHistory = nullptr; - GetPointerPenInfo getPointerPenInfo = nullptr; - GetPointerPenInfoHistory getPointerPenInfoHistory = nullptr; - SkipPointerFrameMessages skipPointerFrameMessages = nullptr; - - // Windows Vista onwards - SetProcessDPIAware setProcessDPIAware = nullptr; - - // Windows 10 version 1607 onwards - GetDpiForWindow getDpiForWindow = nullptr; - - // Windows 10 version 1703 onwards - SetProcessDpiAwarenessContext setProcessDpiAwarenessContext = nullptr; - - // Clipboard listeners are present on Windows Vista onwards - // but missing in MinGW 4.9 stub libs. Can be removed in MinGW 5. - AddClipboardFormatListener addClipboardFormatListener = nullptr; - RemoveClipboardFormatListener removeClipboardFormatListener = nullptr; - - // Rotation API - GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr; - SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr; - - AdjustWindowRectExForDpi adjustWindowRectExForDpi = nullptr; - EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr; - GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr; - GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr; - SystemParametersInfoForDpi systemParametersInfoForDpi = nullptr; -}; - -// Shell scaling library (Windows 8.1 onwards) -struct QWindowsShcoreDLL { - void init(); - inline bool isValid() const { return getProcessDpiAwareness && setProcessDpiAwareness && getDpiForMonitor; } - - typedef HRESULT (WINAPI *GetProcessDpiAwareness)(HANDLE,int *); - typedef HRESULT (WINAPI *SetProcessDpiAwareness)(int); - typedef HRESULT (WINAPI *GetDpiForMonitor)(HMONITOR,int,UINT *,UINT *); - - GetProcessDpiAwareness getProcessDpiAwareness = nullptr; - SetProcessDpiAwareness setProcessDpiAwareness = nullptr; - GetDpiForMonitor getDpiForMonitor = nullptr; -}; - class QWindowsContext { Q_DISABLE_COPY_MOVE(QWindowsContext) public: + using HandleBaseWindowHash = QHash<HWND, QWindowsWindow *>; enum SystemInfoFlags { @@ -204,8 +88,6 @@ public: static QWindowsContext *instance(); - static QString windowsErrorMessage(unsigned long errorCode); - void addWindow(HWND, QWindowsWindow *w); void removeWindow(HWND); @@ -233,11 +115,10 @@ public: QSharedPointer<QWindowCreationContext> windowCreationContext() const; static void setTabletAbsoluteRange(int a); - void setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness); - static int processDpiAwareness(); - void setProcessDpiV2Awareness(); - static bool isDarkMode(); + static bool setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness); + static QtWindows::DpiAwareness processDpiAwareness(); + static QtWindows::DpiAwareness windowDpiAwareness(HWND hwnd); void setDetectAltGrModifier(bool a); @@ -245,18 +126,16 @@ public: unsigned systemInfo() const; bool useRTLExtensions() const; - QList<int> possibleKeys(const QKeyEvent *e) const; + QPlatformKeyMapper *keyMapper() const; + + HandleBaseWindowHash &windows(); static bool isSessionLocked(); - QWindowsMimeConverter &mimeConverter() const; + QWindowsMimeRegistry &mimeConverter() const; QWindowsScreenManager &screenManager(); QWindowsTabletSupport *tabletSupport() const; - static QWindowsUser32DLL user32dll; - static QWindowsShcoreDLL shcoredll; - - static QByteArray comErrorString(HRESULT hr); bool asyncExpose() const; void setAsyncExpose(bool value); diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index fe8b6f7085..b416886120 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QT_NO_CURSOR #include "qwindowscursor.h" @@ -50,6 +14,7 @@ #include <QtGui/qscreen.h> #include <QtGui/private/qguiapplication_p.h> // getPixmapCursor() #include <QtGui/private/qhighdpiscaling_p.h> +#include <QtGui/private/qpixmap_win_p.h> #include <QtCore/private/qwinregistry_p.h> #include <QtCore/qdebug.h> @@ -65,8 +30,7 @@ static bool initResources() QT_BEGIN_NAMESPACE -Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0); -Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap); +using namespace Qt::Literals::StringLiterals; /*! \class QWindowsCursorCacheKey @@ -79,10 +43,10 @@ QWindowsPixmapCursorCacheKey::QWindowsPixmapCursorCacheKey(const QCursor &c) : bitmapCacheKey(c.pixmap().cacheKey()), maskCacheKey(0) { if (!bitmapCacheKey) { - Q_ASSERT(!c.bitmap(Qt::ReturnByValue).isNull()); - Q_ASSERT(!c.mask(Qt::ReturnByValue).isNull()); - bitmapCacheKey = c.bitmap(Qt::ReturnByValue).cacheKey(); - maskCacheKey = c.mask(Qt::ReturnByValue).cacheKey(); + Q_ASSERT(!c.bitmap().isNull()); + Q_ASSERT(!c.mask().isNull()); + bitmapCacheKey = c.bitmap().cacheKey(); + maskCacheKey = c.mask().cacheKey(); } } @@ -141,14 +105,16 @@ static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits, hotSpot.setX(width / 2); if (hotSpot.y() < 0) hotSpot.setY(height / 2); - const int n = qMax(1, width / 8); - QScopedArrayPointer<uchar> xBits(new uchar[height * n]); - QScopedArrayPointer<uchar> xMask(new uchar[height * n]); + // a ddb is word aligned, QImage depends on bow it was created + const auto bplDdb = qMax(1, ((width + 15) >> 4) << 1); + const auto bplImg = int(bbits.bytesPerLine()); + QScopedArrayPointer<uchar> xBits(new uchar[height * bplDdb]); + QScopedArrayPointer<uchar> xMask(new uchar[height * bplDdb]); int x = 0; for (int i = 0; i < height; ++i) { const uchar *bits = bbits.constScanLine(i); const uchar *mask = mbits.constScanLine(i); - for (int j = 0; j < n; ++j) { + for (int j = 0; j < bplImg && j < bplDdb; ++j) { uchar b = bits[j]; uchar m = mask[j]; if (invb) @@ -159,6 +125,11 @@ static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits, xMask[x] = b ^ m; ++x; } + for (int i = bplImg; i < bplDdb; ++i) { + xBits[x] = 0; + xMask[x] = 0; + ++x; + } } return CreateCursor(GetModuleHandle(nullptr), hotSpot.x(), hotSpot.y(), width, height, xBits.data(), xMask.data()); @@ -167,17 +138,17 @@ static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits, // Create a cursor from image and mask of the format QImage::Format_Mono. static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1) { - Q_ASSERT(cursor.shape() == Qt::BitmapCursor && !cursor.bitmap(Qt::ReturnByValue).isNull()); - QImage bbits = cursor.bitmap(Qt::ReturnByValue).toImage(); - QImage mbits = cursor.mask(Qt::ReturnByValue).toImage(); + Q_ASSERT(cursor.shape() == Qt::BitmapCursor && !cursor.bitmap().isNull()); + QImage bbits = cursor.bitmap().toImage(); + QImage mbits = cursor.mask().toImage(); scaleFactor /= bbits.devicePixelRatio(); if (!qFuzzyCompare(scaleFactor, 1)) { const QSize scaledSize = (QSizeF(bbits.size()) * scaleFactor).toSize(); bbits = bbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); mbits = mbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } - bbits = bbits.convertToFormat(QImage::Format_Mono); - mbits = mbits.convertToFormat(QImage::Format_Mono); + bbits = std::move(bbits).convertToFormat(QImage::Format_Mono); + mbits = std::move(mbits).convertToFormat(QImage::Format_Mono); const bool invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); const bool invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm); @@ -474,8 +445,8 @@ QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursor if (!bestFit) return PixmapCursor(); - const QPixmap rawImage(QStringLiteral(":/qt-project.org/windows/cursors/images/") + - QString::fromLatin1(bestFit->fileName)); + const QPixmap rawImage(":/qt-project.org/windows/cursors/images/"_L1 + + QLatin1StringView(bestFit->fileName)); return PixmapCursor(rawImage, QPoint(bestFit->hotSpotX, bestFit->hotSpotY)); } #endif // !QT_NO_IMAGEFORMAT_PNG @@ -652,6 +623,11 @@ void QWindowsCursor::clearOverrideCursor() SetCursor(m_overriddenCursor); m_overriddenCursor = m_overrideCursor = nullptr; } + auto &windows = QWindowsContext::instance()->windows(); + for (auto it = windows.cbegin(), end = windows.cend(); it != end; ++it) { + if (it.value()->screen() == m_screen) + it.value()->setFlag(QWindowsWindow::RestoreOverrideCursor); + } } QPoint QWindowsCursor::mousePosition() diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h index fc2a111aac..d5c0388d1d 100644 --- a/src/plugins/platforms/windows/qwindowscursor.h +++ b/src/plugins/platforms/windows/qwindowscursor.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSCURSOR_H #define QWINDOWSCURSOR_H diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 0312bde563..0ce0b0e2a7 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -1,49 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #define QT_NO_URL_CAST_FROM_STRING 1 -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0601 -#endif - -#include "qwindowscombase.h" +#include <QtCore/qt_windows.h> #include "qwindowsdialoghelpers.h" #include "qwindowscontext.h" @@ -70,44 +30,18 @@ #include <QtCore/qmutex.h> #include <QtCore/quuid.h> #include <QtCore/qtemporaryfile.h> -#include <QtCore/private/qsystemlibrary_p.h> +#include <QtCore/private/qfunctions_win_p.h> +#include <QtCore/private/qsystemerror_p.h> +#include <QtCore/private/qcomobject_p.h> #include <algorithm> #include <vector> -#include <QtCore/qt_windows.h> - // #define USE_NATIVE_COLOR_DIALOG /* Testing purposes only */ QT_BEGIN_NAMESPACE -#ifndef QT_NO_DEBUG_STREAM -/* Output UID (IID, CLSID) as C++ constants. - * The constants are contained in the Windows SDK libs, but not for MinGW. */ -static inline QString guidToString(const GUID &g) -{ - QString rc; - QTextStream str(&rc); - str.setIntegerBase(16); - str.setNumberFlags(str.numberFlags() | QTextStream::ShowBase); - str << '{' << g.Data1 << ", " << g.Data2 << ", " << g.Data3; - str.setFieldWidth(2); - str.setFieldAlignment(QTextStream::AlignRight); - str.setPadChar(u'0'); - str << ",{" << g.Data4[0] << ", " << g.Data4[1] << ", " << g.Data4[2] << ", " << g.Data4[3] - << ", " << g.Data4[4] << ", " << g.Data4[5] << ", " << g.Data4[6] << ", " << g.Data4[7] - << "}};"; - return rc; -} - -inline QDebug operator<<(QDebug d, const GUID &g) -{ - QDebugStateSaver saver(d); - d.nospace(); - d << guidToString(g); - return d; -} -#endif // !QT_NO_DEBUG_STREAM +using namespace Qt::StringLiterals; // Return an allocated wchar_t array from a QString, reserve more memory if desired. static wchar_t *qStringToWCharArray(const QString &s, size_t reserveSize = 0) @@ -142,6 +76,22 @@ void eatMouseMove() qCDebug(lcQpaDialogs) << __FUNCTION__ << "triggered=" << (msg.message == WM_MOUSEMOVE); } +HWND getHWND(IFileDialog *fileDialog) +{ + IOleWindow *oleWindow = nullptr; + if (FAILED(fileDialog->QueryInterface(IID_IOleWindow, reinterpret_cast<void **>(&oleWindow)))) { + qCWarning(lcQpaDialogs, "Native file dialog: unable to query IID_IOleWindow interface."); + return HWND(0); + } + + HWND result(0); + if (FAILED(oleWindow->GetWindow(&result))) + qCWarning(lcQpaDialogs, "Native file dialog: unable to get dialog's window."); + + oleWindow->Release(); + return result; +} + } // namespace QWindowsDialogs /*! @@ -150,7 +100,7 @@ void eatMouseMove() Base classes for native dialogs (using the CLSID-based dialog interfaces "IFileDialog", etc. available from Windows - Vista on) that mimick the behaviour of their QDialog + Vista on) that mimic the behavior of their QDialog counterparts as close as possible. Instances of derived classes are controlled by @@ -214,20 +164,25 @@ private: */ template <class BaseClass> +QWindowsDialogHelperBase<BaseClass>::~QWindowsDialogHelperBase() +{ + hide(); + cleanupThread(); +} + +template <class BaseClass> void QWindowsDialogHelperBase<BaseClass>::cleanupThread() { - if (m_thread) { // Thread may be running if the dialog failed to close. - if (m_thread->isRunning()) - m_thread->wait(500); - if (m_thread->isRunning()) { - m_thread->terminate(); - m_thread->wait(300); - if (m_thread->isRunning()) - qCCritical(lcQpaDialogs) <<__FUNCTION__ << "Failed to terminate thread."; - else - qCWarning(lcQpaDialogs) << __FUNCTION__ << "Thread terminated."; - } - delete m_thread; + if (m_thread) { + // Thread may be running if the dialog failed to close. Give it a bit + // to exit, but let it be a memory leak if that fails. We must not + // terminate the thread, it might be stuck in Comdlg32 or an IModalWindow + // implementation, and we might end up dead-locking the application if the thread + // holds a mutex or critical section. + if (m_thread->wait(500)) + delete m_thread; + else + qCCritical(lcQpaDialogs) <<__FUNCTION__ << "Thread failed to finish."; m_thread = nullptr; } } @@ -254,7 +209,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(); } @@ -283,6 +238,7 @@ private: void QWindowsDialogThread::run() { qCDebug(lcQpaDialogs) << '>' << __FUNCTION__; + QComHelper comInit(COINIT_APARTMENTTHREADED); m_dialog->exec(m_owner); qCDebug(lcQpaDialogs) << '<' << __FUNCTION__; } @@ -339,47 +295,13 @@ void QWindowsDialogHelperBase<BaseClass>::stopTimer() } } -// Find a file dialog window created by IFileDialog by process id, window -// title and class, which starts with a hash '#'. - -struct FindDialogContext -{ - explicit FindDialogContext(const QString &titleIn) - : title(qStringToWCharArray(titleIn)), processId(GetCurrentProcessId()), hwnd(nullptr) {} - - const QScopedArrayPointer<wchar_t> title; - const DWORD processId; - HWND hwnd; // contains the HWND of the window found. -}; - -static BOOL QT_WIN_CALLBACK findDialogEnumWindowsProc(HWND hwnd, LPARAM lParam) -{ - auto *context = reinterpret_cast<FindDialogContext *>(lParam); - DWORD winPid = 0; - GetWindowThreadProcessId(hwnd, &winPid); - if (winPid != context->processId) - return TRUE; - wchar_t buf[256]; - if (!RealGetWindowClass(hwnd, buf, sizeof(buf)/sizeof(wchar_t)) || buf[0] != L'#') - return TRUE; - if (!GetWindowTextW(hwnd, buf, sizeof(buf)/sizeof(wchar_t)) || wcscmp(buf, context->title.data()) != 0) - return TRUE; - context->hwnd = hwnd; - return FALSE; -} - -static inline HWND findDialogWindow(const QString &title) -{ - FindDialogContext context(title); - EnumWindows(findDialogEnumWindowsProc, reinterpret_cast<LPARAM>(&context)); - return context.hwnd; -} - template <class BaseClass> void QWindowsDialogHelperBase<BaseClass>::hide() { - if (m_nativeDialog) + if (m_nativeDialog) { m_nativeDialog->close(); + m_nativeDialog.clear(); + } m_ownerWindow = nullptr; } @@ -501,7 +423,7 @@ inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer<QFile class QWindowsNativeFileDialogBase; -class QWindowsNativeFileDialogEventHandler : public QWindowsComBase<IFileDialogEvents> +class QWindowsNativeFileDialogEventHandler : public QComObject<IFileDialogEvents> { Q_DISABLE_COPY_MOVE(QWindowsNativeFileDialogEventHandler) public: @@ -594,8 +516,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 @@ -634,8 +566,8 @@ QUrl QWindowsShellItem::url() const if (urlV.isValid()) return urlV; // Last resort: encode the absolute desktop parsing id as data URL - const QString data = QStringLiteral("data:text/plain;base64,") - + QLatin1String(desktopAbsoluteParsing().toLatin1().toBase64()); + const QString data = "data:text/plain;base64,"_L1 + + QLatin1StringView(desktopAbsoluteParsing().toLatin1().toBase64()); return QUrl(data); } @@ -668,14 +600,14 @@ QWindowsShellItem::IShellItems QWindowsShellItem::itemsFromItemArray(IShellItemA bool QWindowsShellItem::copyData(QIODevice *out, QString *errorMessage) { if (!canStream()) { - *errorMessage = QLatin1String("Item not streamable"); + *errorMessage = "Item not streamable"_L1; return false; } IStream *istream = nullptr; HRESULT hr = m_item->BindToHandler(nullptr, BHID_Stream, IID_PPV_ARGS(&istream)); if (FAILED(hr)) { - *errorMessage = QLatin1String("BindToHandler() failed: ") - + QLatin1String(QWindowsContext::comErrorString(hr)); + *errorMessage = "BindToHandler() failed: "_L1 + + QSystemError::windowsComString(hr); return false; } enum : ULONG { bufSize = 102400 }; @@ -691,8 +623,8 @@ bool QWindowsShellItem::copyData(QIODevice *out, QString *errorMessage) } istream->Release(); if (hr != S_OK && hr != S_FALSE) { - *errorMessage = QLatin1String("Read() failed: ") - + QLatin1String(QWindowsContext::comErrorString(hr)); + *errorMessage = "Read() failed: "_L1 + + QSystemError::windowsComString(hr); return false; } return true; @@ -1055,7 +987,7 @@ static QList<FilterSpec> filterSpecs(const QStringList &filters, #if QT_CONFIG(regularexpression) filterSpec.filter.replace(filterSeparatorRE, separator); #else - filterSpec.filter.replace(QLatin1Char(' '), QLatin1Char(';')); + filterSpec.filter.replace(u' ', u';'); #endif filterSpec.description = filterString; if (hideFilterDetails && openingParenPos != -1) { // Do not show pattern in description @@ -1269,7 +1201,7 @@ void QWindowsNativeFileDialogBase::close() m_fileDialog->Close(S_OK); // IFileDialog::Close() does not work unless invoked from a callback. // Try to find the window and send it a WM_CLOSE in addition. - const HWND hwnd = findDialogWindow(m_title); + const HWND hwnd = QWindowsDialogs::getHWND(m_fileDialog); qCDebug(lcQpaDialogs) << __FUNCTION__ << "closing" << hwnd; if (hwnd && IsWindowVisible(hwnd)) PostMessageW(hwnd, WM_CLOSE, 0, 0); @@ -1405,7 +1337,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); } @@ -1427,7 +1359,7 @@ QString tempFilePattern(QString name) int lastDot = name.lastIndexOf(u'.'); if (lastDot < 0) lastDot = name.size(); - name.insert(lastDot, QStringLiteral("_XXXXXX")); + name.insert(lastDot, "_XXXXXX"_L1); for (int i = lastDot - 1; i >= 0; --i) { if (!validFileNameCharacter(name.at(i))) @@ -1441,14 +1373,14 @@ QString tempFilePattern(QString name) static QString createTemporaryItemCopy(QWindowsShellItem &qItem, QString *errorMessage) { if (!qItem.canStream()) { - *errorMessage = QLatin1String("Item not streamable"); + *errorMessage = "Item not streamable"_L1; return QString(); } QTemporaryFile targetFile(tempFilePattern(qItem.normalDisplay())); targetFile.setAutoRemove(false); if (!targetFile.open()) { - *errorMessage = QLatin1String("Cannot create temporary file: ") + *errorMessage = "Cannot create temporary file: "_L1 + targetFile.errorString(); return QString(); } @@ -1619,7 +1551,7 @@ QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() if (!info.isDir()) result->selectFile(info.fileName()); } else { - result->selectFile(url.path()); // TODO url.fileName() once it exists + result->selectFile(url.fileName()); } } // No need to select initialNameFilter if mode is Dir @@ -1653,7 +1585,7 @@ void QWindowsFileDialogHelper::selectFile(const QUrl &fileName) qCDebug(lcQpaDialogs) << __FUNCTION__ << fileName.toString(); if (hasNativeDialog()) // Might be invoked from the QFileDialog constructor. - nativeFileDialog()->selectFile(fileName.toLocalFile()); // ## should use QUrl::fileName() once it exists + nativeFileDialog()->selectFile(fileName.fileName()); } QList<QUrl> QWindowsFileDialogHelper::selectedFiles() const @@ -1707,9 +1639,6 @@ public slots: void close() override {} private: - typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW); - typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW); - explicit QWindowsXpNativeFileDialog(const OptionsPtr &options, const QWindowsFileDialogSharedData &data); void populateOpenFileName(OPENFILENAME *ofn, HWND owner) const; QList<QUrl> execExistingDir(HWND owner); @@ -1719,27 +1648,11 @@ private: QString m_title; QPlatformDialogHelper::DialogCode m_result; QWindowsFileDialogSharedData m_data; - - static PtrGetOpenFileNameW m_getOpenFileNameW; - static PtrGetSaveFileNameW m_getSaveFileNameW; }; -QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = nullptr; -QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = nullptr; - QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data) { - // GetOpenFileNameW() GetSaveFileName() are resolved - // dynamically as not to create a dependency on Comdlg32, which - // is used on XP only. - if (!m_getOpenFileNameW) { - QSystemLibrary library(QStringLiteral("Comdlg32")); - m_getOpenFileNameW = (PtrGetOpenFileNameW)(library.resolve("GetOpenFileNameW")); - m_getSaveFileNameW = (PtrGetSaveFileNameW)(library.resolve("GetSaveFileNameW")); - } - if (m_getOpenFileNameW && m_getSaveFileNameW) - return new QWindowsXpNativeFileDialog(options, data); - return nullptr; + return new QWindowsXpNativeFileDialog(options, data); } QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options, @@ -1780,14 +1693,6 @@ static int QT_WIN_CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UIN return dialog->existingDirCallback(hwnd, uMsg, lParam); } -/* The correct declaration of the SHGetPathFromIDList symbol is - * being used in mingw-w64 as of r6215, which is a v3 snapshot. */ -#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) -typedef ITEMIDLIST *qt_LpItemIdList; -#else -using qt_LpItemIdList = PIDLIST_ABSOLUTE; -#endif - int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam) { switch (uMsg) { @@ -1801,7 +1706,7 @@ int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM break; case BFFM_SELCHANGED: { wchar_t path[MAX_PATH]; - const bool ok = SHGetPathFromIDList(reinterpret_cast<qt_LpItemIdList>(lParam), path) + const bool ok = SHGetPathFromIDList(reinterpret_cast<PIDLIST_ABSOLUTE>(lParam), path) && path[0]; SendMessage(hwnd, BFFM_ENABLEOK, ok ? 1 : 0, 1); } @@ -1823,7 +1728,7 @@ QList<QUrl> QWindowsXpNativeFileDialog::execExistingDir(HWND owner) bi.lpfn = xpFileDialogGetExistingDirCallbackProc; bi.lParam = LPARAM(this); QList<QUrl> selectedFiles; - if (qt_LpItemIdList pItemIDList = SHBrowseForFolder(&bi)) { + if (const auto pItemIDList = SHBrowseForFolder(&bi)) { wchar_t path[MAX_PATH]; path[0] = 0; if (SHGetPathFromIDList(pItemIDList, path) && path[0]) @@ -1903,7 +1808,7 @@ QList<QUrl> QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedF populateOpenFileName(&ofn, owner); QList<QUrl> result; const bool isSave = m_options->acceptMode() == QFileDialogOptions::AcceptSave; - if (isSave ? m_getSaveFileNameW(&ofn) : m_getOpenFileNameW(&ofn)) { + if (isSave ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) { *selectedFilterIndex = ofn.nFilterIndex - 1; const QString dir = QDir::cleanPath(QString::fromWCharArray(ofn.lpstrFile)); result.push_back(QUrl::fromLocalFile(dir)); @@ -2045,8 +1950,6 @@ QWindowsNativeColorDialog::QWindowsNativeColorDialog(const SharedPointerColor &c void QWindowsNativeColorDialog::doExec(HWND owner) { - typedef BOOL (WINAPI *ChooseColorWType)(LPCHOOSECOLORW); - CHOOSECOLOR chooseColor; ZeroMemory(&chooseColor, sizeof(chooseColor)); chooseColor.lStructSize = sizeof(chooseColor); @@ -2059,18 +1962,9 @@ void QWindowsNativeColorDialog::doExec(HWND owner) m_customColors[c] = qColorToCOLORREF(QColor(qCustomColors[c])); chooseColor.rgbResult = qColorToCOLORREF(*m_color); chooseColor.Flags = CC_FULLOPEN | CC_RGBINIT; - static ChooseColorWType chooseColorW = 0; - if (!chooseColorW) { - QSystemLibrary library(QStringLiteral("Comdlg32")); - chooseColorW = (ChooseColorWType)library.resolve("ChooseColorW"); - } - if (chooseColorW) { - m_code = chooseColorW(&chooseColor) ? - QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected; - QWindowsDialogs::eatMouseMove(); - } else { - m_code = QPlatformDialogHelper::Rejected; - } + m_code = ChooseColorW(&chooseColor) ? + QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected; + QWindowsDialogs::eatMouseMove(); if (m_code == QPlatformDialogHelper::Accepted) { *m_color = COLORREFToQColor(chooseColor.rgbResult); for (int c= 0; c < customColorCount; ++c) diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.h b/src/plugins/platforms/windows/qwindowsdialoghelpers.h index 55167ad36d..64f40bc3a9 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.h +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSDIALOGHELPER_H #define QWINDOWSDIALOGHELPER_H @@ -67,7 +31,7 @@ class QWindowsDialogHelperBase : public BaseClass Q_DISABLE_COPY_MOVE(QWindowsDialogHelperBase) public: using QWindowsNativeDialogBasePtr = QSharedPointer<QWindowsNativeDialogBase>; - ~QWindowsDialogHelperBase() { cleanupThread(); } + ~QWindowsDialogHelperBase(); void exec() override; bool show(Qt::WindowFlags windowFlags, diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index f46adf9132..c6f55c3509 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -1,42 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include <QtCore/qt_windows.h> #include "qwindowsdrag.h" #include "qwindowscontext.h" #include "qwindowsscreen.h" @@ -45,7 +10,6 @@ #endif #include "qwindowsintegration.h" #include "qwindowsdropdataobject.h" -#include <QtCore/qt_windows.h> #include "qwindowswindow.h" #include "qwindowsmousehandler.h" #include "qwindowscursor.h" @@ -64,6 +28,8 @@ #include <QtCore/qdebug.h> #include <QtCore/qbuffer.h> #include <QtCore/qpoint.h> +#include <QtCore/qpointer.h> +#include <QtCore/private/qcomobject_p.h> #include <shlobj.h> @@ -203,7 +169,7 @@ static Qt::MouseButtons lastButtons = Qt::NoButton; \internal */ -class QWindowsOleDropSource : public QWindowsComBase<IDropSource> +class QWindowsOleDropSource : public QComObject<IDropSource> { public: enum Mode { @@ -384,7 +350,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) } else { if (buttons && !m_currentButtons) { m_currentButtons = buttons; - } else if (!(m_currentButtons & buttons)) { // Button changed: Complete Drop operation. + } else if (m_currentButtons != buttons) { // Button changed: Complete Drop operation. result = DRAGDROP_S_DROP; } } @@ -420,7 +386,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) } /*! - \brief Give feedback: Change cursor accoding to action. + \brief Give feedback: Change cursor according to action. */ QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP @@ -562,7 +528,8 @@ QWindowsOleDropTarget::DragLeave() qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window; - lastModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + lastModifiers = keyMapper->queryKeyboardModifiers(); lastButtons = QWindowsMouseHandler::queryMouseButtons(); QWindowSystemInterface::handleDrag(m_window, nullptr, QPoint(), Qt::IgnoreAction, @@ -647,7 +614,6 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, */ bool QWindowsDrag::m_canceled = false; -bool QWindowsDrag::m_dragging = false; QWindowsDrag::QWindowsDrag() = default; @@ -680,6 +646,86 @@ 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 bool hasMouseCapture = underMouse && static_cast<QWindowsWindow *>(underMouse->handle())->hasMouseCapture(); + const HWND hwnd = hasMouseCapture ? 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? @@ -695,10 +741,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) const DWORD allowedEffects = translateToWinDragEffects(possibleActions); qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x" << 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); - QWindowsDrag::m_dragging = false; + const HRESULT r = startDoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect(); if (r == DRAGDROP_S_DROP) { if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) { diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h index 33b583b479..a2d0e54044 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.h +++ b/src/plugins/platforms/windows/qwindowsdrag.h @@ -1,51 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSDRAG_H #define QWINDOWSDRAG_H -#include "qwindowscombase.h" #include "qwindowsinternalmimedata.h" #include <qpa/qplatformdrag.h> #include <QtGui/qpixmap.h> #include <QtGui/qdrag.h> +#include <QtCore/private/qcomobject_p.h> struct IDropTargetHelper; @@ -59,7 +23,7 @@ public: IDataObject *retrieveDataObject() const override; }; -class QWindowsOleDropTarget : public QWindowsComBase<IDropTarget> +class QWindowsOleDropTarget : public QComObject<IDropTarget> { public: explicit QWindowsOleDropTarget(QWindow *w); @@ -94,7 +58,6 @@ public: static QWindowsDrag *instance(); void cancelDrag() override { QWindowsDrag::m_canceled = true; } static bool isCanceled() { return QWindowsDrag::m_canceled; } - static bool isDragging() { return QWindowsDrag::m_dragging; } IDataObject *dropDataObject() const { return m_dropDataObject; } void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; } @@ -105,7 +68,6 @@ public: private: static bool m_canceled; - static bool m_dragging; QWindowsDropMimeData m_dropData; IDataObject *m_dropDataObject = nullptr; diff --git a/src/plugins/platforms/windows/qwindowsdropdataobject.cpp b/src/plugins/platforms/windows/qwindowsdropdataobject.cpp index a06a14a980..629291640f 100644 --- a/src/plugins/platforms/windows/qwindowsdropdataobject.cpp +++ b/src/plugins/platforms/windows/qwindowsdropdataobject.cpp @@ -1,50 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsdropdataobject.h" #include <QtCore/qurl.h> #include <QtCore/qmimedata.h> -#include "qwindowsmime.h" +#include "qwindowsmimeregistry.h" QT_BEGIN_NAMESPACE +using namespace Qt::Literals::StringLiterals; + /*! \class QWindowsDropDataObject \brief QWindowsOleDataObject subclass specialized for handling Drag&Drop. @@ -89,11 +55,11 @@ bool QWindowsDropDataObject::shouldIgnore(LPFORMATETC pformatetc) const QMimeData *dropData = mimeData(); if (dropData && dropData->formats().size() == 1 && dropData->hasUrls()) { - QString formatName = QWindowsMimeConverter::clipboardFormatName(pformatetc->cfFormat); + QString formatName = QWindowsMimeRegistry::clipboardFormatName(pformatetc->cfFormat); if (pformatetc->cfFormat == CF_UNICODETEXT || pformatetc->cfFormat == CF_TEXT - || formatName == QStringLiteral("UniformResourceLocator") - || formatName == QStringLiteral("UniformResourceLocatorW")) { + || formatName == "UniformResourceLocator"_L1 + || formatName == "UniformResourceLocatorW"_L1) { const auto urls = dropData->urls(); return std::all_of(urls.cbegin(), urls.cend(), [] (const QUrl &u) { return u.isLocalFile(); }); } diff --git a/src/plugins/platforms/windows/qwindowsdropdataobject.h b/src/plugins/platforms/windows/qwindowsdropdataobject.h index 407c809243..b74a3dbc7e 100644 --- a/src/plugins/platforms/windows/qwindowsdropdataobject.h +++ b/src/plugins/platforms/windows/qwindowsdropdataobject.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSDROPDATAOBJECT_H #define QWINDOWSDROPDATAOBJECT_H diff --git a/src/plugins/platforms/windows/qwindowsgdiintegration.cpp b/src/plugins/platforms/windows/qwindowsgdiintegration.cpp index bb24060dbe..14fd03fc1f 100644 --- a/src/plugins/platforms/windows/qwindowsgdiintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsgdiintegration.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsgdiintegration.h" #include "qwindowscontext.h" @@ -45,10 +9,6 @@ #include <QtCore/qdebug.h> #include <QtGui/private/qpixmap_raster_p.h> -#if QT_CONFIG(opengl) -#include <QtOpenGL/qpa/qplatformbackingstoreopenglsupport.h> -#endif - QT_BEGIN_NAMESPACE class QWindowsGdiIntegrationPrivate diff --git a/src/plugins/platforms/windows/qwindowsgdiintegration.h b/src/plugins/platforms/windows/qwindowsgdiintegration.h index 74ce3ffd49..fbb6b7460d 100644 --- a/src/plugins/platforms/windows/qwindowsgdiintegration.h +++ b/src/plugins/platforms/windows/qwindowsgdiintegration.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSGDIINTEGRATION_H #define QWINDOWSGDIINTEGRATION_H diff --git a/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp b/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp index f2418b0e60..78325c88d1 100644 --- a/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsgdinativeinterface.h" #include "qwindowsbackingstore.h" diff --git a/src/plugins/platforms/windows/qwindowsgdinativeinterface.h b/src/plugins/platforms/windows/qwindowsgdinativeinterface.h index c86d3cbb47..7ef1244db3 100644 --- a/src/plugins/platforms/windows/qwindowsgdinativeinterface.h +++ b/src/plugins/platforms/windows/qwindowsgdinativeinterface.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSGDINATIVEINTERFACE_H #define QWINDOWSGDINATIVEINTERFACE_H diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index 1a7be0d271..5ca52c2c19 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsglcontext.h" #include "qwindowscontext.h" @@ -47,7 +11,7 @@ #include <QtGui/qcolorspace.h> #include <QtGui/qguiapplication.h> #include <qpa/qplatformnativeinterface.h> - +#include <private/qsystemlibrary_p.h> #include <algorithm> #include <wingdi.h> @@ -162,19 +126,25 @@ QFunctionPointer QWindowsOpengl32DLL::resolve(const char *name) bool QWindowsOpengl32DLL::init(bool softwareRendering) { - const QByteArray opengl32 = QByteArrayLiteral("opengl32.dll"); - const QByteArray swopengl = QByteArrayLiteral("opengl32sw.dll"); + const QByteArray opengl32 = QByteArrayLiteral("opengl32"); + const QByteArray swopengl = QByteArrayLiteral("opengl32sw"); + bool useSystemLib = false; QByteArray openglDll = qgetenv("QT_OPENGL_DLL"); - if (openglDll.isEmpty()) + if (openglDll.isEmpty()) { openglDll = softwareRendering ? swopengl : opengl32; + useSystemLib = !softwareRendering; + } openglDll = openglDll.toLower(); m_nonOpengl32 = openglDll != opengl32; qCDebug(lcQpaGl) << "Qt: Using WGL and OpenGL from" << openglDll; - m_lib = ::LoadLibraryA(openglDll.constData()); + if (useSystemLib) + m_lib = QSystemLibrary::load((wchar_t*)(QString::fromLatin1(openglDll).utf16())); + else + m_lib = LoadLibraryA(openglDll.constData()); if (!m_lib) { qErrnoWarning(::GetLastError(), "Failed to load %s", openglDll.constData()); return false; @@ -184,7 +154,7 @@ bool QWindowsOpengl32DLL::init(bool softwareRendering) // Load opengl32.dll always. GDI functions like ChoosePixelFormat do // GetModuleHandle for opengl32.dll and behave differently (and call back into // opengl32) when the module is present. This is fine for dummy contexts and windows. - ::LoadLibraryA("opengl32.dll"); + QSystemLibrary::load(L"opengl32"); } wglCreateContext = reinterpret_cast<HGLRC (WINAPI *)(HDC)>(resolve("wglCreateContext")); @@ -574,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; @@ -590,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; } @@ -606,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) { @@ -615,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; } @@ -1291,12 +1262,11 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface) // Do we already have a DC entry for that window? auto *window = static_cast<QWindowsWindow *>(surface); - window->aboutToMakeCurrent(); const HWND hwnd = window->handle(); if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, hwnd)) { // Repeated calls to wglMakeCurrent when vsync is enabled in the driver will // often result in 100% cpuload. This check is cheap and avoids the problem. - // This is reproducable on NVidia cards and Intel onboard chips. + // This is reproducible on NVidia cards and Intel onboard chips. if (QOpenGLStaticContext::opengl32.wglGetCurrentContext() == contextData->renderingContext && QOpenGLStaticContext::opengl32.wglGetCurrentDC() == contextData->hdc) { return true; diff --git a/src/plugins/platforms/windows/qwindowsglcontext.h b/src/plugins/platforms/windows/qwindowsglcontext.h index 0e8383e5c0..bf71959853 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.h +++ b/src/plugins/platforms/windows/qwindowsglcontext.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSGLCONTEXT_H #define QWINDOWSGLCONTEXT_H diff --git a/src/plugins/platforms/windows/qwindowsiconengine.cpp b/src/plugins/platforms/windows/qwindowsiconengine.cpp new file mode 100644 index 0000000000..5e5ca22ec1 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsiconengine.cpp @@ -0,0 +1,394 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsiconengine.h" + +#include <QtCore/qoperatingsystemversion.h> +#include <QtGui/qguiapplication.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpalette.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +QString QWindowsIconEngine::glyphs() const +{ + if (!QFontInfo(m_iconFont).exactMatch()) + return {}; + + static constexpr std::pair<QLatin1StringView, QStringView> glyphMap[] = { + {"address-book-new"_L1, u"\ue780"}, + {"application-exit"_L1, u"\ue8bb"}, + {"appointment-new"_L1, u"\ue878"}, + {"call-start"_L1, u"\uf715"}, + {"call-stop"_L1, u"\uf405"}, + {"contact-new"_L1, u"\ue8fa"}, + {"document-new"_L1, u"\ue8a5"}, + {"document-open"_L1, u"\ue8e5"}, + {"document-open-recent"_L1, u"\ue823"}, + {"document-page-setup"_L1, u"\ue7c3"}, + {"document-print"_L1, u"\ue749"}, + {"document-print-preview"_L1, u"\ue956"}, + {"document-properties"_L1, u"\ue90f"}, + {"document-revert"_L1, u"\ue7a7"}, // ? + {"document-save"_L1, u"\ue74e"}, // or e78c? + {"document-save-as"_L1, u"\ue792"}, + {"document-send"_L1, u"\ue724"}, + {"edit-clear"_L1, u"\ue894"}, + {"edit-copy"_L1, u"\ue8c8"}, + {"edit-cut"_L1, u"\ue8c6"}, + {"edit-delete"_L1, u"\ue74d"}, + {"edit-find"_L1, u"\ue721"}, + //{"edit-find-replace"_L1, u"\u"}, + {"edit-paste"_L1, u"\ue77f"}, + {"edit-redo"_L1, u"\ue7a6"}, + {"edit-select-all"_L1, u"\ue8b3"}, + {"edit-undo"_L1, u"\ue7a7"}, + {"folder-new"_L1, u"\ue8f4"}, + //{"format-indent-less"_L1, u"\u"}, + //{"format-indent-more"_L1, u"\u"}, + {"format-justify-center"_L1, u"\ue8e3"}, + //{"format-justify-fill"_L1, u"\ue235"}, + {"format-justify-left"_L1, u"\ue8e4"}, + {"format-justify-right"_L1, u"\ue8e2"}, + //{"format-text-direction-ltr"_L1, u"\ue247"}, + //{"format-text-direction-rtl"_L1, u"\ue248"}, + {"format-text-bold"_L1, u"\ue8dd"}, + {"format-text-italic"_L1, u"\ue8db"}, + {"format-text-underline"_L1, u"\ue8dc"}, + {"format-text-strikethrough"_L1, u"\uede0"}, + //{"go-bottom"_L1,u"\ue258"}, + {"go-down"_L1,u"\ue74b"}, + //{"go-first"_L1, u"\ue5dc"}, + {"go-home"_L1, u"\ue80f"}, + // {"go-jump"_L1, u"\uf719"}, + //{"go-last"_L1, u"\ue5dd"}, + {"go-next"_L1, u"\ue893"}, + {"go-previous"_L1, u"\ue892"}, + //{"go-top"_L1, u"\ue25a"}, + {"go-up"_L1, u"\ue74a"}, + {"help-about"_L1, u"\ue946"}, + //{"help-contents"_L1, u"\ue8de"}, + {"help-faq"_L1, u"\ue897"}, + {"insert-image"_L1, u"\ue946"}, + {"insert-link"_L1, u"\ue71b"}, + //{"insert-object"_L1, u"\u"}, + //{"insert-text"_L1, u"\uf827"}, + {"list-add"_L1, u"\ue710"}, + {"list-remove"_L1, u"\ue738"}, + {"mail-forward"_L1, u"\ue89c"}, + //{"mail-mark-important"_L1, u"\ue937"}, + //{"mail-mark-junk"_L1, u"\u"}, + //{"mail-mark-notjunk"_L1, u"\u"}, + {"mail-mark-read"_L1, u"\ue8c3"}, + //{"mail-mark-unread"_L1, u"\ue9bc"}, + {"mail-message-new"_L1, u"\ue70f"}, + {"mail-reply-all"_L1, u"\ue8c2"}, + {"mail-reply-sender"_L1, u"\ue8ca"}, + {"mail-send"_L1, u"\ue724"}, + //{"mail-send-receive"_L1, u"\u"}, + {"media-eject"_L1, u"\uf847"}, + {"media-playback-pause"_L1, u"\ue769"}, + {"media-playback-start"_L1, u"\ue768"}, + {"media-playback-stop"_L1, u"\ue71a"}, + {"media-record"_L1, u"\ue7c8"}, + {"media-seek-backward"_L1, u"\ueb9e"}, + {"media-seek-forward"_L1, u"\ueb9d"}, + {"media-skip-backward"_L1, u"\ue892"}, + {"media-skip-forward"_L1, u"\ue893"}, + //{"object-flip-horizontal"_L1, u"\u"}, + //{"object-flip-vertical"_L1, u"\u"}, + {"object-rotate-left"_L1, u"\ue80c"}, + {"object-rotate-right"_L1, u"\ue80d"}, + //{"process-stop"_L1, u"\ue5c9"}, + {"system-lock-screen"_L1, u"\uee3f"}, + {"system-log-out"_L1, u"\uf3b1"}, + //{"system-run"_L1, u"\u"}, + {"system-search"_L1, u"\ue721"}, + {"system-reboot"_L1, u"\ue777"}, // unsure? + {"system-shutdown"_L1, u"\ue7e8"}, + {"tools-check-spelling"_L1, u"\uf87b"}, + {"view-fullscreen"_L1, u"\ue740"}, + {"view-refresh"_L1, u"\ue72c"}, + {"view-restore"_L1, u"\ue777"}, + //{"view-sort-ascending"_L1, u"\ue25a"}, + //{"view-sort-descending"_L1, u"\ue258"}, + {"window-close"_L1, u"\ue8bb"}, + {"window-new"_L1, u"\ue78b"}, + {"zoom-fit-best"_L1, u"\ue9a6"}, + {"zoom-in"_L1, u"\ue8a3"}, + //{"zoom-original"_L1, u"\u"}, + {"zoom-out"_L1, u"\ue71f"}, + + {"process-working"_L1, u"\ue9f3"}, + + {"accessories-calculator"_L1, u"\ue8ef"}, + {"accessories-character-map"_L1, u"\uf2b7"}, + {"accessories-dictionary"_L1, u"\ue82d"}, + //{"accessories-text-editor"_L1, u"\ue262"}, + {"help-browser"_L1, u"\ue897"}, + {"multimedia-volume-control"_L1, u"\ue767"}, + {"preferences-desktop-accessibility"_L1, u"\ue776"}, + {"preferences-desktop-font"_L1, u"\ue8d2"}, + {"preferences-desktop-keyboard"_L1, u"\ue765"}, + {"preferences-desktop-locale"_L1, u"\uf2b7"}, + //{"preferences-desktop-multimedia"_L1, u"\uea75"}, + {"preferences-desktop-screensaver"_L1, u"\uf182"}, + //{"preferences-desktop-theme"_L1, u"\uf560"}, + //{"preferences-desktop-wallpaper"_L1, u"\ue1bc"}, + {"system-file-manager"_L1, u"\uec50"}, + //{"system-software-install"_L1, u"\ueb71"}, + {"system-software-update"_L1, u"\uecc5"}, + {"utilities-system-monitor"_L1, u"\ue7f4"}, + //{"utilities-terminal"_L1, u"\ueb8e"}, + + //{"applications-accessories"_L1, u"\u"}, + {"applications-development"_L1, u"\uec7a"}, + //{"applications-engineering"_L1, u"\uea3d"}, + {"applications-games"_L1, u"\ue7fc"}, + //{"applications-graphics"_L1, u"\u"}, + {"applications-internet"_L1, u"\ue774"}, + {"applications-multimedia"_L1, u"\uea69"}, + //{"applications-office"_L1, u"\u"}, + //{"applications-other"_L1, u"\u"}, + //{"applications-science"_L1, u"\uea4b"}, + {"applications-system"_L1, u"\ue770"}, + //{"applications-utilities"_L1, u"\u"}, + //{"preferences-desktop"_L1, u"\ueb97"}, + //{"preferences-desktop-peripherals"_L1, u"\u"}, + //{"preferences-desktop-personal"_L1, u"\uf835"}, + //{"preferences-other"_L1, u"\u"}, + //{"preferences-system"_L1, u"\ue8b8"}, + //{"preferences-system-network"_L1, u"\ue894"}, + {"system-help"_L1, u"\ue946"}, + + {"audio-card"_L1, u"\ue8d6"}, + {"audio-input-microphone"_L1, u"\ue720"}, + {"battery"_L1, u"\ue83f"}, + {"camera-photo"_L1, u"\ue722"}, + {"camera-video"_L1, u"\ue714"}, + {"camera-web"_L1, u"\ue8b8"}, + {"computer"_L1, u"\ue7f8"}, // or e7fb? + {"drive-harddisk"_L1, u"\ueda2"}, + {"drive-optical"_L1, u"\ue958"}, + //{"drive-removable-media"_L1, u"\u"}, + //{"input-gaming"_L1, u"\u"}, + {"input-keyboard"_L1, u"\ue92e"}, + {"input-mouse"_L1, u"\ue962"}, + {"input-tablet"_L1, u"\ue70a"}, + {"media-flash"_L1, u"\ue88e"}, + //{"media-floppy"_L1, u"\u"}, + {"media-optical"_L1, u"\ue958"}, + {"media-tape"_L1, u"\ue96a"}, + //{"modem"_L1, u"\u"}, + //{"multimedia-player"_L1, u"\u"}, + {"network-wired"_L1, u"\ue968"}, + {"network-wireless"_L1, u"\ue701"}, + //{"pda"_L1, u"\u"}, + {"phone"_L1, u"\ue717"}, + {"printer"_L1, u"\ue749"}, + {"scanner"_L1, u"\ue8fe"}, + //{"video-display"_L1, u"\uf06a"}, + + {"emblem-default"_L1, u"\uf56d"}, + {"emblem-documents"_L1, u"\ue8a5"}, + {"emblem-downloads"_L1, u"\ue896"}, + {"emblem-favorite"_L1, u"\ue734"}, + {"emblem-important"_L1, u"\ue8c9"}, + {"emblem-mail"_L1, u"\ue715"}, + {"emblem-photos"_L1, u"\ue91b"}, + //{"emblem-readonly"_L1, u"\u"}, + {"emblem-shared"_L1, u"\ue902"}, + {"emblem-symbolic-link"_L1, u"\ue71b"}, + {"emblem-synchronized"_L1, u"\uedab"}, + {"emblem-system"_L1, u"\ue770"}, + //{"emblem-unreadable"_L1, u"\u"}, + + {"folder"_L1, u"\ue8b7"}, + //{"folder-remote"_L1, u"\u"}, + //{"network-server"_L1, u"\ue875"}, + //{"network-workgroup"_L1, u"\ue1a0"}, + {"start-here"_L1, u"\ue8fc"}, // unsure + {"user-bookmarks"_L1, u"\ue8a4"}, + //{"user-desktop"_L1, u"\ue30a"}, + {"user-home"_L1, u"\ue80f"}, + {"user-trash"_L1, u"\ue74d"}, + + //{"appointment-missed"_L1, u"\ue615"}, + //{"appointment-soon"_L1, u"\uf540"}, + {"audio-volume-high"_L1, u"\ue995"}, + {"audio-volume-low"_L1, u"\ue993"}, + {"audio-volume-medium"_L1, u"\ue994"}, + {"audio-volume-muted"_L1, u"\ue992"}, + //{"battery-caution"_L1, u"\ue19c"}, + {"battery-low"_L1, u"\ue851"}, // ? + {"dialog-error"_L1, u"\ue783"}, + {"dialog-information"_L1, u"\ue946"}, + //{"dialog-password"_L1, u"\uf042"}, + {"dialog-question"_L1, u"\uf142"}, // unsure + {"dialog-warning"_L1, u"\ue7ba"}, + //{"folder-drag-accept"_L1, @u"\ue9a3"}, + {"folder-open"_L1, u"\ue838"}, + //{"folder-visiting"_L1, u"\ue8a7"}, + //{"image-loading"_L1, u"\ue41a"}, + //{"image-missing"_L1, u"\ue3ad"}, + {"mail-attachment"_L1, u"\ue723"}, + //{"mail-unread"_L1, u"\uf18a"}, + //{"mail-read"_L1, u"\uf18c"}, + {"mail-replied"_L1, u"\ue8ca"}, + //{"mail-signed"_L1, u"\u"}, + //{"mail-signed-verified"_L1, u"\u"}, + {"media-playlist-repeat"_L1, u"\ue8ee"}, + {"media-playlist-shuffle"_L1, u"\ue8b1"}, + //{"network-error"_L1, u"\uead9"}, + //{"network-idle"_L1, u"\ue51f"}, + {"network-offline"_L1, u"\uf384"}, + //{"network-receive"_L1, u"\ue2c0"}, + //{"network-transmit"_L1, u"\ue2c3"}, + //{"network-transmit-receive"_L1, u"\uca18"}, + //{"printer-error"_L1, u"\uf7a0"}, + //{"printer-printing"_L1, u"\uf7a1"}, + //{"security-high"_L1, u"\ue32a"}, + //{"security-medium"_L1, u"\ue9e0"}, + //{"security-low"_L1, u"\uf012"}, + //{"software-update-available"_L1, u"\ue923"}, + //{"software-update-urgent"_L1, u"\uf05a"}, + {"sync-error"_L1, u"\uea6a"}, + {"sync-synchronizing"_L1, u"\ue895"}, + //{"task-due"_L1, u"\u"}, + //{"task-past-due"_L1, u"\u"}, + {"user-available"_L1, u"\ue8cf"}, + //{"user-away"_L1, u"\ue510"}, + //{"user-idle"_L1, u"\u"}, + //{"user-offline"_L1, u"\uf7b3"}, + //{"user-trash-full"_L1, u"\ue872"}, //delete + //{"user-trash-full"_L1, u"\ue92b"}, //delete_forever + {"weather-clear"_L1, u"\ue706"}, + //{"weather-clear-night"_L1, u"\uf159"}, + //{"weather-few-clouds"_L1, u"\uf172"}, + //{"weather-few-clouds-night"_L1, u"\uf174"}, + //{"weather-fog"_L1, u"\ue818"}, + {"weather-overcast"_L1, u"\ue753"}, + //{"weather-severe-alert"_L1, u"\ue002"}, //warning + //{"weather-showers"_L1, u"\uf176"}, + //{"weather-showers-scattered"_L1, u"\u"}, + //{"weather-snow"_L1, u"\ue80f"}, //snowing + //{"weather-storm"_L1, u"\uf070"}, + }; + + const auto it = std::find_if(std::begin(glyphMap), std::end(glyphMap), [this](const auto &c){ + return c.first == m_iconName; + }); + + return it != std::end(glyphMap) ? it->second.toString() + : (m_iconName.length() == 1 ? m_iconName : QString()); +} + +namespace { +auto iconFontFamily() +{ + static const bool isWindows11 = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11; + return isWindows11 ? u"Segoe Fluent Icons"_s + : u"Segoe MDL2 Assets"_s; +} +} + +QWindowsIconEngine::QWindowsIconEngine(const QString &iconName) + : m_iconName(iconName), m_iconFont(iconFontFamily()) + , m_glyphs(glyphs()) +{ +} + +QWindowsIconEngine::~QWindowsIconEngine() +{} + +QIconEngine *QWindowsIconEngine::clone() const +{ + return new QWindowsIconEngine(m_iconName); +} + +QString QWindowsIconEngine::key() const +{ + return u"QWindowsIconEngine"_s; +} + +QString QWindowsIconEngine::iconName() +{ + return m_iconName; +} + +bool QWindowsIconEngine::isNull() +{ + if (m_glyphs.isEmpty()) + return true; + + const QChar c0 = m_glyphs.at(0); + const QFontMetrics fontMetrics(m_iconFont); + if (c0.category() == QChar::Other_Surrogate && m_glyphs.size() > 1) + return !fontMetrics.inFontUcs4(QChar::surrogateToUcs4(c0, m_glyphs.at(1))); + return !fontMetrics.inFont(c0); +} + +QList<QSize> QWindowsIconEngine::availableSizes(QIcon::Mode, QIcon::State) +{ + return {{16, 16}, {24, 24}, {48, 48}, {128, 128}}; +} + +QSize QWindowsIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + return QIconEngine::actualSize(size, mode, state); +} + +QPixmap QWindowsIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + return scaledPixmap(size, mode, state, 1.0); +} + +QPixmap QWindowsIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) +{ + const quint64 cacheKey = calculateCacheKey(mode, state); + if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) { + m_pixmap = QPixmap(size * scale); + m_pixmap.fill(Qt::transparent); + m_pixmap.setDevicePixelRatio(scale); + + QPainter painter(&m_pixmap); + paint(&painter, QRect(QPoint(), size), mode, state); + + m_cacheKey = cacheKey; + } + + return m_pixmap; +} + +void QWindowsIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) +{ + Q_UNUSED(state); + + painter->save(); + QFont renderFont(m_iconFont); + renderFont.setPixelSize(rect.height()); + painter->setFont(renderFont); + + QPalette palette; + switch (mode) { + case QIcon::Active: + painter->setPen(palette.color(QPalette::Active, QPalette::Text)); + break; + case QIcon::Normal: + painter->setPen(palette.color(QPalette::Active, QPalette::Text)); + break; + case QIcon::Disabled: + painter->setPen(palette.color(QPalette::Disabled, QPalette::Text)); + break; + case QIcon::Selected: + painter->setPen(palette.color(QPalette::Active, QPalette::HighlightedText)); + break; + } + + painter->drawText(rect, Qt::AlignCenter, m_glyphs); + painter->restore(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsiconengine.h b/src/plugins/platforms/windows/qwindowsiconengine.h new file mode 100644 index 0000000000..3c6cbddb8b --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsiconengine.h @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSICONENGINE_H +#define QWINDOWSICONENGINE_H + +#include <QtCore/qt_windows.h> + +#include <QtGui/qfont.h> +#include <QtGui/qiconengine.h> + +QT_BEGIN_NAMESPACE + +class QWindowsIconEngine : public QIconEngine +{ +public: + QWindowsIconEngine(const QString &iconName); + ~QWindowsIconEngine(); + QIconEngine *clone() const override; + QString key() const override; + QString iconName() override; + bool isNull() override; + + QList<QSize> availableSizes(QIcon::Mode, QIcon::State) override; + QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override; + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override; + QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override; + void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override; + +private: + static constexpr quint64 calculateCacheKey(QIcon::Mode mode, QIcon::State state) + { + return (quint64(mode) << 32) | state; + } + + QString glyphs() const; + + const QString m_iconName; + const QFont m_iconFont; + const QString m_glyphs; + mutable QPixmap m_pixmap; + mutable quint64 m_cacheKey = {}; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSICONENGINE_H diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp index 03be0b9451..0281025b5b 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsinputcontext.h" #include "qwindowscontext.h" @@ -47,7 +11,6 @@ #include <QtCore/qobject.h> #include <QtCore/qrect.h> #include <QtCore/qtextboundaryfinder.h> -#include <QtCore/qoperatingsystemversion.h> #include <QtGui/qevent.h> #include <QtGui/qtextformat.h> @@ -198,7 +161,6 @@ bool QWindowsInputContext::hasCapability(Capability capability) const void QWindowsInputContext::reset() { - QPlatformInputContext::reset(); if (!m_compositionContext.hwnd) return; qCDebug(lcQpaInputMethods) << __FUNCTION__; @@ -275,25 +237,9 @@ void QWindowsInputContext::showInputPanel() if (!m_caretCreated && m_transparentBitmap) m_caretCreated = CreateCaret(platformWindow->handle(), m_transparentBitmap, 0, 0); - // For some reason, the on-screen keyboard is only triggered on the Surface - // with Windows 10 if the Windows IME is (re)enabled _after_ the caret is shown. if (m_caretCreated) { cursorRectChanged(); - // We only call ShowCaret() on Windows 10 after 1703 as in earlier versions - // the caret would actually be visible (QTBUG-74492) and the workaround for - // the Surface seems unnecessary there anyway. But leave it hidden for IME. - // Only trigger the native OSK if the Qt OSK is not in use. - static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE"); - bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard); - if ((imModuleEmpty && !nativeVKDisabled) - && QOperatingSystemVersion::current() - >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 16299)) { - ShowCaret(platformWindow->handle()); - } else { - HideCaret(platformWindow->handle()); - } - setWindowsImeEnabled(platformWindow, false); - setWindowsImeEnabled(platformWindow, true); + ShowCaret(platformWindow->handle()); } } @@ -338,7 +284,6 @@ void QWindowsInputContext::update(Qt::InputMethodQueries queries) { if (queries & Qt::ImEnabled) updateEnabled(); - QPlatformInputContext::update(queries); } void QWindowsInputContext::cursorRectChanged() @@ -731,7 +676,7 @@ int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv) reconv->dwTargetStrOffset = reconv->dwCompStrOffset; auto *pastReconv = reinterpret_cast<ushort *>(reconv + 1); std::copy(surroundingText.utf16(), surroundingText.utf16() + surroundingText.size(), - QT_MAKE_UNCHECKED_ARRAY_ITERATOR(pastReconv)); + pastReconv); return memSize; } diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.h b/src/plugins/platforms/windows/qwindowsinputcontext.h index 857706bcb9..e7eafb9ea5 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.h +++ b/src/plugins/platforms/windows/qwindowsinputcontext.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSINPUTCONTEXT_H #define QWINDOWSINPUTCONTEXT_H diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 822c11d0f2..aa6be266da 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsintegration.h" #include "qwindowswindow.h" @@ -84,6 +48,11 @@ #include <QtCore/qdebug.h> #include <QtCore/qvariant.h> +#include <QtCore/qoperatingsystemversion.h> +#include <QtCore/private/qfunctions_win_p.h> + +#include <wrl.h> + #include <limits.h> #if !defined(QT_NO_OPENGL) @@ -92,6 +61,16 @@ #include "qwindowsopengltester.h" +#if QT_CONFIG(cpp_winrt) +# include <QtCore/private/qt_winrtbase_p.h> +# include <winrt/Windows.UI.Notifications.h> +# include <winrt/Windows.Data.Xml.Dom.h> +# include <winrt/Windows.Foundation.h> +# include <winrt/Windows.UI.ViewManagement.h> +#endif + +#include <memory> + static inline void initOpenGlBlacklistResources() { Q_INIT_RESOURCE(openglblacklists); @@ -99,38 +78,7 @@ static inline void initOpenGlBlacklistResources() QT_BEGIN_NAMESPACE -/*! - \class QWindowsIntegration - \brief QPlatformIntegration implementation for Windows. - \internal - - \section1 Programming Considerations - - The platform plugin should run on Desktop Windows from Windows XP onwards - and Windows Embedded. - - It should compile with: - \list - \li Microsoft Visual Studio 2013 or later (using the Microsoft Windows SDK, - (\c Q_CC_MSVC). - \li Stock \l{http://mingw.org/}{MinGW} (\c Q_CC_MINGW). - This version ships with headers that are missing a lot of WinAPI. - \li MinGW distributions using GCC 4.7 or higher and a recent MinGW-w64 runtime API, - such as \l{http://tdm-gcc.tdragon.net/}{TDM-GCC}, or - \l{http://mingwbuilds.sourceforge.net/}{MinGW-builds} - (\c Q_CC_MINGW and \c __MINGW64_VERSION_MAJOR indicating the version). - MinGW-w64 provides more complete headers (compared to stock MinGW from mingw.org), - including a considerable part of the Windows SDK. - \endlist - - When using a function from the WinAPI, the minimum supported Windows version - and Windows Embedded support should be checked. If the function is not supported - on Windows XP or is not present in the MinGW-headers, it should be dynamically - resolved. For this purpose, QWindowsContext has static structs like - QWindowsUser32DLL and QWindowsShell32DLL. All function pointers should go to - these structs to avoid lookups in several places. - -*/ +using namespace Qt::StringLiterals; struct QWindowsIntegrationPrivate { @@ -161,7 +109,7 @@ struct QWindowsIntegrationPrivate }; template <typename IntType> -bool parseIntOption(const QString ¶meter,const QLatin1String &option, +bool parseIntOption(const QString ¶meter,const QLatin1StringView &option, IntType minimumValue, IntType maximumValue, IntType *target) { const int valueLength = parameter.size() - option.size() - 1; @@ -171,7 +119,7 @@ bool parseIntOption(const QString ¶meter,const QLatin1String &option, const auto valueRef = QStringView{parameter}.right(valueLength); const int value = valueRef.toInt(&ok); if (ok) { - if (value >= minimumValue && value <= maximumValue) + if (value >= int(minimumValue) && value <= int(maximumValue)) *target = static_cast<IntType>(value); else { qWarning() << "Value" << value << "for option" << option << "out of range" @@ -188,14 +136,14 @@ using DarkModeHandling = QNativeInterface::Private::QWindowsApplication::DarkMod static inline unsigned parseOptions(const QStringList ¶mList, int *tabletAbsoluteRange, - QtWindows::ProcessDpiAwareness *dpiAwareness, + QtWindows::DpiAwareness *dpiAwareness, DarkModeHandling *darkModeHandling) { unsigned options = 0; for (const QString ¶m : paramList) { if (param.startsWith(u"fontengine=")) { - if (param.endsWith(u"directwrite")) { - options |= QWindowsIntegration::FontDatabaseDirectWrite; + if (param.endsWith(u"gdi")) { + options |= QWindowsIntegration::FontDatabaseGDI; } else if (param.endsWith(u"freetype")) { options |= QWindowsIntegration::FontDatabaseFreeType; } else if (param.endsWith(u"native")) { @@ -217,9 +165,10 @@ static inline unsigned parseOptions(const QStringList ¶mList, options |= QWindowsIntegration::DontUseColorFonts; } else if (param == u"nomousefromtouch") { options |= QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch; - } else if (parseIntOption(param, QLatin1String("verbose"), 0, INT_MAX, &QWindowsContext::verbose) - || parseIntOption(param, QLatin1String("tabletabsoluterange"), 0, INT_MAX, tabletAbsoluteRange) - || parseIntOption(param, QLatin1String("dpiawareness"), QtWindows::ProcessDpiUnaware, QtWindows::ProcessPerMonitorV2DpiAware, dpiAwareness)) { + } else if (parseIntOption(param, "verbose"_L1, 0, INT_MAX, &QWindowsContext::verbose) + || parseIntOption(param, "tabletabsoluterange"_L1, 0, INT_MAX, tabletAbsoluteRange) + || parseIntOption(param, "dpiawareness"_L1, QtWindows::DpiAwareness::Invalid, + QtWindows::DpiAwareness::PerMonitorVersion2, dpiAwareness)) { } else if (param == u"menus=native") { options |= QWindowsIntegration::AlwaysUseNativeMenus; } else if (param == u"menus=none") { @@ -228,8 +177,11 @@ static inline unsigned parseOptions(const QStringList ¶mList, options |= QWindowsIntegration::DontUseWMPointer; } else if (param == u"reverse") { options |= QWindowsIntegration::RtlEnabled; + } else if (param == u"darkmode=0") { + *darkModeHandling = {}; } else if (param == u"darkmode=1") { darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeWindowFrames); + darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeStyle, false); } else if (param == u"darkmode=2") { darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeWindowFrames); darkModeHandling->setFlag(DarkModeHandlingFlag::DarkModeStyle); @@ -245,13 +197,12 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr initOpenGlBlacklistResources(); static bool dpiAwarenessSet = false; - static bool hasDpiAwarenessContext = QWindowsContext::user32dll.setProcessDpiAwarenessContext != nullptr; // Default to per-monitor-v2 awareness (if available) - QtWindows::ProcessDpiAwareness dpiAwareness = hasDpiAwarenessContext ? - QtWindows::ProcessPerMonitorV2DpiAware : QtWindows::ProcessPerMonitorDpiAware; + QtWindows::DpiAwareness dpiAwareness = QtWindows::DpiAwareness::PerMonitorVersion2; int tabletAbsoluteRange = -1; - DarkModeHandling darkModeHandling; + DarkModeHandling darkModeHandling = DarkModeHandlingFlag::DarkModeWindowFrames + | DarkModeHandlingFlag::DarkModeStyle; m_options = ::parseOptions(paramList, &tabletAbsoluteRange, &dpiAwareness, &darkModeHandling); q->setDarkModeHandling(darkModeHandling); QWindowsFontDatabase::setFontOptions(m_options); @@ -262,21 +213,13 @@ 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)) { - - // DpiAwareV2 requires using new API - if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware && hasDpiAwarenessContext) { - m_context.setProcessDpiV2Awareness(); - qCDebug(lcQpaWindows) - << __FUNCTION__ << "DpiAwareness: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2"; - } else { - m_context.setProcessDpiAwareness(dpiAwareness); - qCDebug(lcQpaWindows) - << __FUNCTION__ << "DpiAwareness=" << dpiAwareness - << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness(); - } + m_context.setProcessDpiAwareness(dpiAwareness); + qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness + << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness(); } dpiAwarenessSet = true; } @@ -302,7 +245,7 @@ QWindowsIntegration::QWindowsIntegration(const QStringList ¶mList) : #if QT_CONFIG(clipboard) d->m_clipboard.registerViewer(); #endif - d->m_context.screenManager().handleScreenChanges(); + d->m_context.screenManager().initialize(); d->m_context.setDetectAltGrModifier((d->m_options & DetectAltGrModifier) != 0); } @@ -313,9 +256,9 @@ QWindowsIntegration::~QWindowsIntegration() void QWindowsIntegration::initialize() { - QString icStr = QPlatformInputContextFactory::requested(); - icStr.isNull() ? d->m_inputContext.reset(new QWindowsInputContext) - : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); + auto icStrs = QPlatformInputContextFactory::requested(); + icStrs.isEmpty() ? d->m_inputContext.reset(new QWindowsInputContext) + : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); } bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) const @@ -343,6 +286,8 @@ bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) co return true; case SwitchableWidgetComposition: return false; // QTBUG-68329 QTBUG-53515 QTBUG-54734 + case BackingStoreStaticContents: + return true; default: return QPlatformIntegration::hasCapability(cap); } @@ -353,7 +298,7 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons { if (window->type() == Qt::Desktop) { auto *result = new QWindowsDesktopWindow(window); - qCDebug(lcQpaWindows) << "Desktop window:" << window + qCDebug(lcQpaWindow) << "Desktop window:" << window << Qt::showbase << Qt::hex << result->winId() << Qt::noshowbase << Qt::dec << result->geometry(); return result; } @@ -363,15 +308,17 @@ 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, QWindowsWindow::formatWindowTitle(window->title())); - qCDebug(lcQpaWindows).nospace() + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << ' ' << window << "\n Requested: " << requested.geometry << " frame incl.=" << QWindowsGeometryHint::positionIncludesFrame(window) @@ -408,7 +355,7 @@ QPlatformWindow *QWindowsIntegration::createForeignWindow(QWindow *window, WId n screen = pScreen->screen(); if (screen && screen != window->screen()) window->setScreen(screen); - qCDebug(lcQpaWindows) << "Foreign window:" << window << Qt::showbase << Qt::hex + qCDebug(lcQpaWindow) << "Foreign window:" << window << Qt::showbase << Qt::hex << result->winId() << Qt::noshowbase << Qt::dec << obtainedGeometry << screen; return result; } @@ -476,9 +423,9 @@ QPlatformOpenGLContext *QWindowsIntegration::createPlatformOpenGLContext(QOpenGL { qCDebug(lcQpaGl) << __FUNCTION__ << context->format(); if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) { - QScopedPointer<QWindowsOpenGLContext> result(staticOpenGLContext->createContext(context)); + std::unique_ptr<QWindowsOpenGLContext> result(staticOpenGLContext->createContext(context)); if (result->isValid()) - return result.take(); + return result.release(); } return nullptr; } @@ -508,12 +455,12 @@ QOpenGLContext *QWindowsIntegration::createOpenGLContext(HGLRC ctx, HWND window, return nullptr; if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) { - QScopedPointer<QWindowsOpenGLContext> result(staticOpenGLContext->createContext(ctx, window)); + std::unique_ptr<QWindowsOpenGLContext> result(staticOpenGLContext->createContext(ctx, window)); if (result->isValid()) { auto *context = new QOpenGLContext; context->setShareContext(shareContext); auto *contextPrivate = QOpenGLContextPrivate::get(context); - contextPrivate->adopt(result.take()); + contextPrivate->adopt(result.release()); return context; } } @@ -537,17 +484,17 @@ QWindowsStaticOpenGLContext *QWindowsIntegration::staticOpenGLContext() QPlatformFontDatabase *QWindowsIntegration::fontDatabase() const { if (!d->m_fontDatabase) { -#if QT_CONFIG(directwrite3) - if (d->m_options & QWindowsIntegration::FontDatabaseDirectWrite) - d->m_fontDatabase = new QWindowsDirectWriteFontDatabase; - else -#endif #ifndef QT_NO_FREETYPE if (d->m_options & QWindowsIntegration::FontDatabaseFreeType) d->m_fontDatabase = new QWindowsFontDatabaseFT; else #endif // QT_NO_FREETYPE - d->m_fontDatabase = new QWindowsFontDatabase(); +#if QT_CONFIG(directwrite3) + if (!(d->m_options & (QWindowsIntegration::FontDatabaseGDI | QWindowsIntegration::DontUseDirectWriteFonts))) + d->m_fontDatabase = new QWindowsDirectWriteFontDatabase; + else +#endif + d->m_fontDatabase = new QWindowsFontDatabase; } return d->m_fontDatabase; } @@ -595,14 +542,9 @@ QVariant QWindowsIntegration::styleHint(QPlatformIntegration::StyleHint hint) co return QPlatformIntegration::styleHint(hint); } -Qt::KeyboardModifiers QWindowsIntegration::queryKeyboardModifiers() const -{ - return QWindowsKeyMapper::queryKeyboardModifiers(); -} - -QList<int> QWindowsIntegration::possibleKeys(const QKeyEvent *e) const +QPlatformKeyMapper *QWindowsIntegration::keyMapper() const { - return d->m_context.possibleKeys(e); + return d->m_context.keyMapper(); } #if QT_CONFIG(clipboard) @@ -649,12 +591,12 @@ QAbstractEventDispatcher * QWindowsIntegration::createEventDispatcher() const QStringList QWindowsIntegration::themeNames() const { - return QStringList(QLatin1String(QWindowsTheme::name)); + return QStringList(QLatin1StringView(QWindowsTheme::name)); } QPlatformTheme *QWindowsIntegration::createPlatformTheme(const QString &name) const { - if (name == QLatin1String(QWindowsTheme::name)) + if (name == QLatin1StringView(QWindowsTheme::name)) return new QWindowsTheme; return QPlatformIntegration::createPlatformTheme(name); } @@ -669,6 +611,161 @@ void QWindowsIntegration::beep() const MessageBeep(MB_OK); // For QApplication } +void QWindowsIntegration::setApplicationBadge(qint64 number) +{ + // Clamp to positive numbers, as the Windows API doesn't support negative numbers + number = qMax(0, number); + + // Persist, so we can re-apply it on setting changes and Explorer restart + m_applicationBadgeNumber = number; + + static const bool isWindows11 = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11; + +#if QT_CONFIG(cpp_winrt) + // We prefer the native BadgeUpdater API, that allows us to set a number directly, + // but it requires that the application has a package identity, and also doesn't + // seem to work in all cases on < Windows 11. + QT_TRY { + if (isWindows11 && qt_win_hasPackageIdentity()) { + using namespace winrt::Windows::UI::Notifications; + auto badgeXml = BadgeUpdateManager::GetTemplateContent(BadgeTemplateType::BadgeNumber); + badgeXml.SelectSingleNode(L"//badge/@value").NodeValue(winrt::box_value(winrt::to_hstring(number))); + BadgeUpdateManager::CreateBadgeUpdaterForApplication().Update(BadgeNotification(badgeXml)); + return; + } + } QT_CATCH(...) { + // fall back to win32 implementation + } +#endif + + // Fallback for non-packaged apps, Windows 10, or Qt builds without WinRT/C++ support + + if (!number) { + // Clear badge + setApplicationBadge(QImage()); + return; + } + + const bool isDarkMode = QWindowsTheme::instance()->colorScheme() + == Qt::ColorScheme::Dark; + + QColor badgeColor; + QColor textColor; + +#if QT_CONFIG(cpp_winrt) + if (isWindows11) { + // Match colors used by BadgeUpdater + static const auto fromUIColor = [](winrt::Windows::UI::Color &&color) { + return QColor(color.R, color.G, color.B, color.A); + }; + using namespace winrt::Windows::UI::ViewManagement; + const auto settings = UISettings(); + badgeColor = fromUIColor(settings.GetColorValue(isDarkMode ? + UIColorType::AccentLight2 : UIColorType::Accent)); + textColor = fromUIColor(settings.GetColorValue(UIColorType::Background)); + } +#endif + + if (!badgeColor.isValid()) { + // Fall back to basic badge colors, based on Windows 10 look + badgeColor = isDarkMode ? Qt::black : QColor(220, 220, 220); + badgeColor.setAlphaF(0.5f); + textColor = isDarkMode ? Qt::white : Qt::black; + } + + const auto devicePixelRatio = qApp->devicePixelRatio(); + + static const QSize iconBaseSize(16, 16); + QImage image(iconBaseSize * devicePixelRatio, + QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + + QPainter painter(&image); + + QRect badgeRect = image.rect(); + QPen badgeBorderPen = Qt::NoPen; + if (!isWindows11) { + QColor badgeBorderColor = textColor; + badgeBorderColor.setAlphaF(0.5f); + badgeBorderPen = badgeBorderColor; + badgeRect.adjust(1, 1, -1, -1); + } + painter.setBrush(badgeColor); + painter.setPen(badgeBorderPen); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawEllipse(badgeRect); + + auto pixelSize = qCeil(10.5 * devicePixelRatio); + // Unlike the BadgeUpdater API we're limited by a square + // badge, so adjust the font size when above two digits. + const bool textOverflow = number > 99; + if (textOverflow) + pixelSize *= 0.8; + + QFont font = painter.font(); + font.setPixelSize(pixelSize); + font.setWeight(isWindows11 ? QFont::Medium : QFont::DemiBold); + painter.setFont(font); + + painter.setRenderHint(QPainter::TextAntialiasing, devicePixelRatio > 1); + painter.setPen(textColor); + + auto text = textOverflow ? u"99+"_s : QString::number(number); + painter.translate(textOverflow ? 1 : 0, textOverflow ? 0 : -1); + painter.drawText(image.rect(), Qt::AlignCenter, text); + + painter.end(); + + setApplicationBadge(image); +} + +void QWindowsIntegration::setApplicationBadge(const QImage &image) +{ + QComHelper comHelper; + + using Microsoft::WRL::ComPtr; + + ComPtr<ITaskbarList3> taskbarList; + CoCreateInstance(CLSID_TaskbarList, nullptr, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarList)); + if (!taskbarList) { + // There may not be any windows with a task bar button yet, + // in which case we'll apply the badge once a window with + // a button has been created. + return; + } + + const auto hIcon = image.toHICON(); + + // Apply the icon to all top level windows, since the badge is + // set on an application level. If one of the windows go away + // the other windows will take over in showing the badge. + const auto topLevelWindows = QGuiApplication::topLevelWindows(); + for (auto *topLevelWindow : topLevelWindows) { + if (!topLevelWindow->handle()) + continue; + auto hwnd = reinterpret_cast<HWND>(topLevelWindow->winId()); + taskbarList->SetOverlayIcon(hwnd, hIcon, L""); + } + + DestroyIcon(hIcon); + + // FIXME: Update icon when the application scale factor changes. + // Doing so in response to screen DPI changes is too soon, as the + // task bar is not yet ready for an updated icon, and will just + // result in a blurred icon even if our icon is high-DPI. +} + +void QWindowsIntegration::updateApplicationBadge() +{ + // The system color settings have changed, or we are reacting + // to a task bar button being created for the fist time or after + // Explorer had crashed and re-started. In any case, re-apply the + // badge so that everything is up to date. + if (m_applicationBadgeNumber) + setApplicationBadge(m_applicationBadgeNumber); +} + #if QT_CONFIG(vulkan) QPlatformVulkanInstance *QWindowsIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const { diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index b5449b4a45..c271207741 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSINTEGRATION_H #define QWINDOWSINTEGRATION_H @@ -46,7 +10,9 @@ #include <qpa/qplatformintegration.h> #include <QtCore/qscopedpointer.h> #include <QtGui/private/qwindowsfontdatabase_p.h> +#ifndef QT_NO_OPENGL #include <QtGui/private/qopenglcontext_p.h> +#endif #include <qpa/qplatformopenglcontext.h> QT_BEGIN_NAMESPACE @@ -79,7 +45,7 @@ public: DontUseWMPointer = 0x400, DetectAltGrModifier = 0x800, RtlEnabled = 0x1000, - FontDatabaseDirectWrite = 0x2000 + FontDatabaseGDI = 0x2000 }; explicit QWindowsIntegration(const QStringList ¶mList); @@ -116,8 +82,7 @@ public: QPlatformServices *services() const override; QVariant styleHint(StyleHint hint) const override; - Qt::KeyboardModifiers queryKeyboardModifiers() const override; - QList<int> possibleKeys(const QKeyEvent *e) const override; + QPlatformKeyMapper *keyMapper() const override; static QWindowsIntegration *instance() { return m_instance; } @@ -125,6 +90,10 @@ public: void beep() const override; + void setApplicationBadge(qint64 number) override; + void setApplicationBadge(const QImage &image); + void updateApplicationBadge(); + #if QT_CONFIG(sessionmanager) QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const override; #endif @@ -140,6 +109,8 @@ private: QScopedPointer<QWindowsIntegrationPrivate> d; static QWindowsIntegration *m_instance; + + qint64 m_applicationBadgeNumber = 0; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp b/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp index f5e7465aff..0542473a4b 100644 --- a/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp +++ b/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp @@ -1,45 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsinternalmimedata.h" #include "qwindowscontext.h" -#include "qwindowsmime.h" +#include "qwindowsmimeregistry.h" #include <QtCore/qdebug.h> #include <QtCore/qvariant.h> @@ -58,9 +22,9 @@ The base class introduces new virtuals to obtain and release the instances IDataObject from the clipboard or Drag and Drop and - does conversion using QWindowsMime classes. + does conversion using QWindowsMimeConverter classes. - \sa QInternalMimeData, QWindowsMime, QWindowsMimeConverter + \sa QInternalMimeData, QWindowsMimeConverter, QWindowsMimeRegistry \internal */ @@ -70,7 +34,7 @@ bool QWindowsInternalMimeData::hasFormat_sys(const QString &mime) const if (!pDataObj) return false; - const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); const bool has = mc.converterToMime(mime, pDataObj) != nullptr; releaseDataObject(pDataObj); qCDebug(lcQpaMime) << __FUNCTION__ << mime << has; @@ -83,7 +47,7 @@ QStringList QWindowsInternalMimeData::formats_sys() const if (!pDataObj) return QStringList(); - const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); const QStringList fmts = mc.allMimesForFormats(pDataObj); releaseDataObject(pDataObj); qCDebug(lcQpaMime) << __FUNCTION__ << fmts; @@ -97,7 +61,7 @@ QVariant QWindowsInternalMimeData::retrieveData_sys(const QString &mimeType, QMe return QVariant(); QVariant result; - const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); if (auto converter = mc.converterToMime(mimeType, pDataObj)) result = converter->convertToMime(mimeType, pDataObj, type); releaseDataObject(pDataObj); diff --git a/src/plugins/platforms/windows/qwindowsinternalmimedata.h b/src/plugins/platforms/windows/qwindowsinternalmimedata.h index 998b8c871e..64d4ceabdf 100644 --- a/src/plugins/platforms/windows/qwindowsinternalmimedata.h +++ b/src/plugins/platforms/windows/qwindowsinternalmimedata.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSINTERNALMIME_H #define QWINDOWSINTERNALMIME_H diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 7365401089..ba76cda40b 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowskeymapper.h" #include "qwindowscontext.h" @@ -51,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 @@ -123,9 +88,17 @@ QWindowsKeyMapper::~QWindowsKeyMapper()= default; #define VK_OEM_3 0xC0 #endif -// We not only need the scancode itself but also the extended bit of key messages. Thus we need -// the additional bit when masking the scancode. -enum { scancodeBitmask = 0x1ff }; +// Get scancode from the given message +static constexpr quint32 getScancode(const MSG &msg) +{ + const auto keyFlags = HIWORD(msg.lParam); + quint32 scancode = LOBYTE(keyFlags); + // if extended-key flag is on, the scan code consists of a sequence of two bytes, + // where the first byte has a value of 0xe0. + if ((keyFlags & KF_EXTENDED) != 0) + scancode |= 0xE000; + return scancode; +} // Key recorder ------------------------------------------------------------------------[ start ] -- struct KeyRecord { @@ -567,33 +540,6 @@ QDebug operator<<(QDebug d, const KeyboardLayoutItem &k) d << ')'; return d; } - -// Helpers to format a list of int as Qt key sequence -class formatKeys -{ -public: - explicit formatKeys(const QList<int> &keys) : m_keys(keys) {} - -private: - friend QDebug operator<<(QDebug d, const formatKeys &keys); - const QList<int> &m_keys; -}; - -QDebug operator<<(QDebug d, const formatKeys &k) -{ - QDebugStateSaver saver(d); - d.nospace(); - d << '('; - for (int i =0, size = k.m_keys.size(); i < size; ++i) { - if (i) - d << ", "; - d << QKeySequence(k.m_keys.at(i)); - } - d << ')'; - return d; -} -#else // !QT_NO_DEBUG_STREAM -static int formatKeys(const QList<int> &) { return 0; } #endif // QT_NO_DEBUG_STREAM /** @@ -635,8 +581,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.._ @@ -692,7 +637,7 @@ void QWindowsKeyMapper::updateKeyMap(const MSG &msg) { unsigned char kbdBuffer[256]; // Will hold the complete keyboard state GetKeyboardState(kbdBuffer); - const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; + const quint32 scancode = getScancode(msg); updatePossibleKeyCodes(kbdBuffer, scancode, quint32(msg.wParam)); } @@ -785,6 +730,27 @@ static inline QString messageKeyText(const MSG &msg) return ch.isNull() ? QString() : QString(ch); } +[[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd) +{ + const UINT dpi = GetDpiForWindow(hwnd); + const int captionHeight = GetSystemMetricsForDpi(SM_CYCAPTION, dpi); + if (IsZoomed(hwnd)) + return captionHeight; + // The frame height should also be taken into account if the window + // is not maximized. + const int frameHeight = GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) + + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + return captionHeight + frameHeight; +} + +[[nodiscard]] static inline bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags) +{ + static constexpr const Qt::WindowFlags titleBarHints = + Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint; + return (flags & Qt::WindowSystemMenuHint) && (flags & Qt::WindowTitleHint) && !(flags & titleBarHints) + && (flags & (Qt::FramelessWindowHint | Qt::CustomizeWindowHint)); +} + static void showSystemMenu(QWindow* w) { QWindow *topLevel = QWindowsWindow::topLevelOf(w); @@ -793,41 +759,49 @@ static void showSystemMenu(QWindow* w) if (!menu) return; // no menu for this window -#define enabled (MF_BYCOMMAND | MF_ENABLED) -#define disabled (MF_BYCOMMAND | MF_GRAYED) +#define enabled (MF_BYCOMMAND | MFS_ENABLED) +#define disabled (MF_BYCOMMAND | MFS_GRAYED) - EnableMenuItem(menu, SC_MINIMIZE, (topLevel->flags() & Qt::WindowMinimizeButtonHint)?enabled:disabled); - bool maximized = IsZoomed(topLevelHwnd); + EnableMenuItem(menu, SC_MINIMIZE, (topLevel->flags() & Qt::WindowMinimizeButtonHint) ? enabled : disabled); + const bool maximized = IsZoomed(topLevelHwnd); - EnableMenuItem(menu, SC_MAXIMIZE, ! (topLevel->flags() & Qt::WindowMaximizeButtonHint) || maximized?disabled:enabled); - EnableMenuItem(menu, SC_RESTORE, maximized?enabled:disabled); + EnableMenuItem(menu, SC_MAXIMIZE, !(topLevel->flags() & Qt::WindowMaximizeButtonHint) || maximized ? disabled : enabled); // We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check // this and our menu here would be out-of-sync with the menu produced by mouse-click on the // System Menu, or right-click on the title bar. - EnableMenuItem(menu, SC_SIZE, (topLevel->flags() & Qt::MSWindowsFixedSizeDialogHint) || maximized?disabled:enabled); - EnableMenuItem(menu, SC_MOVE, maximized?disabled:enabled); + EnableMenuItem(menu, SC_SIZE, (topLevel->flags() & Qt::MSWindowsFixedSizeDialogHint) || maximized ? disabled : enabled); + EnableMenuItem(menu, SC_MOVE, maximized ? disabled : enabled); EnableMenuItem(menu, SC_CLOSE, enabled); + EnableMenuItem(menu, SC_RESTORE, maximized ? enabled : disabled); + + // Highlight the first entry in the menu, this is what native Win32 applications usually do. + HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_HILITE); + // Set bold on close menu item - MENUITEMINFO closeItem; - closeItem.cbSize = sizeof(MENUITEMINFO); - closeItem.fMask = MIIM_STATE; - closeItem.fState = MFS_DEFAULT; - SetMenuItemInfo(menu, SC_CLOSE, FALSE, &closeItem); + SetMenuDefaultItem(menu, SC_CLOSE, FALSE); #undef enabled #undef disabled + const QPoint pos = QHighDpi::toNativePixels(topLevel->geometry().topLeft(), topLevel); + const int titleBarOffset = isSystemMenuOffsetNeeded(topLevel->flags()) ? getTitleBarHeight(topLevelHwnd) : 0; const int ret = TrackPopupMenuEx(menu, - TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD, - pos.x(), pos.y(), + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD, + pos.x(), pos.y() + titleBarOffset, topLevelHwnd, nullptr); + + // Remove the highlight of the restore menu item, otherwise when the user right-clicks + // on the title bar, the popuped system menu will also highlight the restore item, which + // is not appropriate, it should only be highlighted if the menu is brought up by keyboard. + HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_UNHILITE); + if (ret) qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, WPARAM(ret), 0); } -static inline void sendExtendedPressRelease(QWindow *w, int k, +static inline void sendExtendedPressRelease(QWindow *w, unsigned long timestamp, int k, Qt::KeyboardModifiers mods, quint32 nativeScanCode, quint32 nativeVirtualKey, @@ -836,8 +810,8 @@ static inline void sendExtendedPressRelease(QWindow *w, int k, bool autorep = false, ushort count = 1) { - QWindowSystemInterface::handleExtendedKeyEvent(w, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); - QWindowSystemInterface::handleExtendedKeyEvent(w, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); + QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); + QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); } /*! @@ -904,7 +878,7 @@ bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, con const int qtKey = int(CmdTbl[cmd]); if (!skipPressRelease) - sendExtendedPressRelease(receiver, qtKey, Qt::KeyboardModifier(state), 0, 0, 0); + sendExtendedPressRelease(receiver, msg.time, qtKey, Qt::KeyboardModifier(state), 0, 0, 0); // QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise, // the keys are not passed to the active media player. # if QT_CONFIG(shortcut) @@ -949,7 +923,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, m_seenAltGr = true; const UINT msgType = msg.message; - const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; + const quint32 scancode = getScancode(msg); auto vk_key = quint32(msg.wParam); quint32 nModifiers = 0; @@ -984,7 +958,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, // A multi-character key or a Input method character // not found by our look-ahead if (msgType == WM_CHAR || msgType == WM_IME_CHAR) { - sendExtendedPressRelease(receiver, 0, Qt::KeyboardModifier(state), scancode, vk_key, nModifiers, messageKeyText(msg), false); + sendExtendedPressRelease(receiver, msg.time, 0, Qt::KeyboardModifier(state), scancode, 0, nModifiers, messageKeyText(msg), false); return true; } @@ -1019,14 +993,14 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, if (dirStatus == VK_LSHIFT && ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL)) || (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) { - sendExtendedPressRelease(receiver, Qt::Key_Direction_L, {}, + sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_L, {}, scancode, vk_key, nModifiers, QString(), false); result = true; dirStatus = 0; } else if (dirStatus == VK_RSHIFT && ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL)) || (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) { - sendExtendedPressRelease(receiver, Qt::Key_Direction_R, {}, + sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_R, {}, scancode, vk_key, nModifiers, QString(), false); result = true; dirStatus = 0; @@ -1220,7 +1194,6 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, switch (code) { case Qt::Key_Escape: case Qt::Key_Tab: - case Qt::Key_Enter: case Qt::Key_F4: return false; // Send the event on to Windows case Qt::Key_Space: @@ -1240,9 +1213,9 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, // so, we have an auto-repeating key if (rec) { if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) { - QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code, + QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code, Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true); - QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code, + QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code, Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true); result = true; } @@ -1268,7 +1241,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, if (msg.wParam == VK_PACKET) code = asciiToKeycode(char(uch.cell()), state); - QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code, + QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code, modifiers, scancode, quint32(msg.wParam), nModifiers, text, false); result =true; bool store = true; @@ -1310,10 +1283,10 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, if ((msg.lParam & 0x40000000) == 0 && Qt::KeyboardModifier(state) == Qt::NoModifier && ((code == Qt::Key_F18) || (code == Qt::Key_F19) || (code == Qt::Key_F20))) { - QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code, + QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code, Qt::MetaModifier, scancode, quint32(msg.wParam), MetaLeft); - QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code, + QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code, Qt::NoModifier, scancode, quint32(msg.wParam), 0); result = true; @@ -1325,7 +1298,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier) code = Qt::Key_Backtab; - QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code, + QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code, Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, (rec ? rec->text : QString()), false); @@ -1347,7 +1320,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, return result; } -Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() +Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() const { Qt::KeyboardModifiers modifiers = Qt::NoModifier; if (GetKeyState(VK_SHIFT) < 0) @@ -1361,9 +1334,9 @@ Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() return modifiers; } -QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const +QList<QKeyCombination> QWindowsKeyMapper::possibleKeyCombinations(const QKeyEvent *e) const { - QList<int> result; + QList<QKeyCombination> result; const quint32 nativeVirtualKey = e->nativeVirtualKey(); @@ -1377,31 +1350,34 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const quint32 baseKey = kbItem.qtKey[0]; Qt::KeyboardModifiers keyMods = e->modifiers(); if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) { - result << (Qt::Key_Enter | keyMods).toCombined(); + result << (Qt::Key_Enter | keyMods); return result; } - result << int(baseKey) + int(keyMods); // The base key is _always_ valid, of course + + // The base key is _always_ valid, of course + result << QKeyCombination::fromCombined(int(baseKey) + int(keyMods)); for (size_t i = 1; i < NumMods; ++i) { Qt::KeyboardModifiers neededMods = ModsTbl[i]; quint32 key = kbItem.qtKey[i]; if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) { const Qt::KeyboardModifiers missingMods = keyMods & ~neededMods; - const int matchedKey = int(key) + int(missingMods); - const auto it = - std::find_if(result.begin(), result.end(), - [key] (int k) { return (k & ~Qt::KeyboardModifierMask) == key; }); + const auto matchedKey = QKeyCombination::fromCombined(int(key) + int(missingMods)); + const auto it = std::find_if(result.begin(), result.end(), + [key](auto keyCombination) { + return keyCombination.key() == key; + }); // QTBUG-67200: Use the match with the least modifiers (prefer // 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 > it->keyboardModifiers()) *it = matchedKey; } } qCDebug(lcQpaEvents) << __FUNCTION__ << e << "nativeVirtualKey=" << Qt::showbase << Qt::hex << e->nativeVirtualKey() << Qt::dec << Qt::noshowbase - << e->modifiers() << kbItem << "\n returns" << formatKeys(result); + << e->modifiers() << kbItem << "\n returns" << result; return result; } diff --git a/src/plugins/platforms/windows/qwindowskeymapper.h b/src/plugins/platforms/windows/qwindowskeymapper.h index b1ada1d373..72b2536ad7 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.h +++ b/src/plugins/platforms/windows/qwindowskeymapper.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSKEYMAPPER_H #define QWINDOWSKEYMAPPER_H @@ -44,6 +8,8 @@ #include <QtCore/qlocale.h> +#include <qpa/qplatformkeymapper.h> + QT_BEGIN_NAMESPACE class QKeyEvent; @@ -69,7 +35,7 @@ struct KeyboardLayoutItem { quint32 qtKey[NumQtKeys]; // Can by any Qt::Key_<foo>, or unicode character }; -class QWindowsKeyMapper +class QWindowsKeyMapper : public QPlatformKeyMapper { Q_DISABLE_COPY_MOVE(QWindowsKeyMapper) public: @@ -89,8 +55,8 @@ public: QWindow *keyGrabber() const { return m_keyGrabber; } void setKeyGrabber(QWindow *w) { m_keyGrabber = w; } - static Qt::KeyboardModifiers queryKeyboardModifiers(); - QList<int> possibleKeys(const QKeyEvent *e) const; + Qt::KeyboardModifiers queryKeyboardModifiers() const override; + QList<QKeyCombination> possibleKeyCombinations(const QKeyEvent *e) const override; private: bool translateKeyEventInternal(QWindow *receiver, MSG msg, bool grab, LRESULT *lResult); diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp index a0ace1f06a..79deeeaea2 100644 --- a/src/plugins/platforms/windows/qwindowsmenu.cpp +++ b/src/plugins/platforms/windows/qwindowsmenu.cpp @@ -1,47 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsmenu.h" #include "qwindowscontext.h" #include "qwindowswindow.h" #include <QtGui/qwindow.h> +#include <QtGui/private/qpixmap_win_p.h> #include <QtCore/qdebug.h> #include <QtCore/qvariant.h> #include <QtCore/qmetaobject.h> @@ -265,8 +230,6 @@ void QWindowsMenuItem::setIcon(const QIcon &icon) updateBitmap(); } -Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0); - void QWindowsMenuItem::updateBitmap() { freeBitmap(); diff --git a/src/plugins/platforms/windows/qwindowsmenu.h b/src/plugins/platforms/windows/qwindowsmenu.h index b94efbe713..6f66180d82 100644 --- a/src/plugins/platforms/windows/qwindowsmenu.h +++ b/src/plugins/platforms/windows/qwindowsmenu.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSMENU_H #define QWINDOWSMENU_H @@ -45,7 +9,6 @@ #include <qpa/qplatformmenu.h> #include <QtCore/qlist.h> -#include <QtCore/qpair.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsmime.h b/src/plugins/platforms/windows/qwindowsmime.h deleted file mode 100644 index e131dba848..0000000000 --- a/src/plugins/platforms/windows/qwindowsmime.h +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWINDOWSMIME_H -#define QWINDOWSMIME_H - -#include <QtGui/private/qwindowsmime_p.h> - -#include <QtCore/qt_windows.h> - -#include <QtCore/qlist.h> -#include <QtCore/qvariant.h> - -QT_BEGIN_NAMESPACE - -class QDebug; -class QMimeData; - -class QWindowsMimeConverter -{ - Q_DISABLE_COPY_MOVE(QWindowsMimeConverter) -public: - using QWindowsMime = QNativeInterface::Private::QWindowsMime; - - QWindowsMimeConverter(); - ~QWindowsMimeConverter(); - - QWindowsMime *converterToMime(const QString &mimeType, IDataObject *pDataObj) const; - QStringList allMimesForFormats(IDataObject *pDataObj) const; - QWindowsMime *converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; - QList<FORMATETC> allFormatsForMime(const QMimeData *mimeData) const; - - // Convenience. - QVariant convertToMime(const QStringList &mimeTypes, IDataObject *pDataObj, QMetaType preferredType, - QString *format = nullptr) const; - - void registerMime(QWindowsMime *mime); - void unregisterMime(QWindowsMime *mime) { m_mimes.removeOne(mime); } - - static int registerMimeType(const QString &mime); - - static QString clipboardFormatName(int cf); - -private: - void ensureInitialized() const; - - mutable QList<QWindowsMime *> m_mimes; - mutable int m_internalMimeCount = 0; -}; - -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug, const FORMATETC &); -QDebug operator<<(QDebug d, IDataObject *); -#endif - -QT_END_NAMESPACE - -#endif // QWINDOWSMIME_H diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmimeregistry.cpp index 3321845411..8d147e8fa0 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmimeregistry.cpp @@ -1,43 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwindowsmime.h" +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsmimeregistry.h" #include "qwindowscontext.h" #include <QtGui/private/qinternalmimedata_p.h> @@ -55,6 +19,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + /* The MSVC compilers allows multi-byte characters, that has the behavior of * that each character gets shifted into position. 0x73524742 below is for MSVC * equivalent to doing 'sRGB', but this does of course not work @@ -96,7 +62,6 @@ struct BMP_BITMAPV5HEADER { DWORD bV5ProfileSize; DWORD bV5Reserved; }; -static const int BMP_BITFIELDS = 3; static const char dibFormatC[] = "dib"; @@ -176,8 +141,20 @@ static bool qt_write_dibv5(QDataStream &s, QImage image) if (s.status() != QDataStream::Ok) return false; + d->write(reinterpret_cast<const char *>(&bi.bV5RedMask), sizeof(bi.bV5RedMask)); + if (s.status() != QDataStream::Ok) + return false; + + d->write(reinterpret_cast<const char *>(&bi.bV5GreenMask), sizeof(bi.bV5GreenMask)); + if (s.status() != QDataStream::Ok) + return false; + + d->write(reinterpret_cast<const char *>(&bi.bV5BlueMask), sizeof(bi.bV5BlueMask)); + if (s.status() != QDataStream::Ok) + return false; + if (image.format() != QImage::Format_ARGB32) - image = image.convertToFormat(QImage::Format_ARGB32); + image = std::move(image).convertToFormat(QImage::Format_ARGB32); auto *buf = new uchar[bpl_bmp]; @@ -323,7 +300,7 @@ QDebug operator<<(QDebug d, const FORMATETC &tc) d << "CF_ENHMETAFILE"; break; default: - d << QWindowsMimeConverter::clipboardFormatName(tc.cfFormat); + d << QWindowsMimeRegistry::clipboardFormatName(tc.cfFormat); break; } d << ", dwAspect=" << tc.dwAspect << ", lindex=" << tc.lindex @@ -356,106 +333,7 @@ QDebug operator<<(QDebug d, IDataObject *dataObj) } #endif // !QT_NO_DEBUG_STREAM -/*! - \class QWindowsMime - \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats. - \internal - - Qt's drag-and-drop and clipboard facilities use the MIME standard. - On X11, this maps trivially to the Xdnd protocol, but on Windows - although some applications use MIME types to describe clipboard - formats, others use arbitrary non-standardized naming conventions, - or unnamed built-in formats of Windows. - - By instantiating subclasses of QWindowsMime that provide conversions - between Windows Clipboard and MIME formats, you can convert - proprietary clipboard formats to MIME formats. - - Qt has predefined support for the following Windows Clipboard formats: - - \table - \header \li Windows Format \li Equivalent MIME type - \row \li \c CF_UNICODETEXT \li \c text/plain - \row \li \c CF_TEXT \li \c text/plain - \row \li \c CF_DIB \li \c{image/xyz}, where \c xyz is - a \l{QImageWriter::supportedImageFormats()}{Qt image format} - \row \li \c CF_HDROP \li \c text/uri-list - \row \li \c CF_INETURL \li \c text/uri-list - \row \li \c CF_HTML \li \c text/html - \endtable - - An example use of this class would be to map the Windows Metafile - clipboard format (\c CF_METAFILEPICT) to and from the MIME type - \c{image/x-wmf}. This conversion might simply be adding or removing - a header, or even just passing on the data. See \l{Drag and Drop} - for more information on choosing and definition MIME types. - - You can check if a MIME type is convertible using canConvertFromMime() and - can perform conversions with convertToMime() and convertFromMime(). - - \sa QWindowsMimeConverter -*/ - - -/*! -\fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const - - Returns \c true if the converter can convert from the \a mimeData to - the format specified in \a formatetc. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const - - Returns \c true if the converter can convert to the \a mimeType from - the available formats in \a pDataObj. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! -\fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const - - Returns the mime type that will be created form the format specified - in \a formatetc, or an empty string if this converter does not support - \a formatetc. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! -\fn QList<FORMATETC> QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const - - Returns a QList of FORMATETC structures representing the different windows clipboard - formats that can be provided for the \a mimeType from the \a mimeData. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj, - QMetaType preferredType) const - - Returns a QVariant containing the converted data for \a mimeType from \a pDataObj. - If possible the QVariant should be of the \a preferredType to avoid needless conversions. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! -\fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const - - Convert the \a mimeData to the format specified in \a formatetc. - The converted data should then be placed in \a pmedium structure. - - Return true if the conversion was successful. - - All subclasses must reimplement this pure virtual function. -*/ - -class QWindowsMimeText : public QNativeInterface::Private::QWindowsMime +class QWindowsMimeText : public QWindowsMimeConverter { public: bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override; @@ -469,7 +347,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(); } /* @@ -560,7 +438,7 @@ QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const { int cf = getCf(formatetc); if (cf == CF_UNICODETEXT || cf == CF_TEXT) - return QStringLiteral("text/plain"); + return u"text/plain"_s; return QString(); } @@ -570,7 +448,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; } @@ -584,7 +463,7 @@ QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pData QByteArray data = getData(CF_UNICODETEXT, pDataObj); if (!data.isEmpty()) { str = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData())); - str.replace(QLatin1String("\r\n"), QLatin1String("\n")); + str.replace("\r\n"_L1, "\n"_L1); } else { data = getData(CF_TEXT, pDataObj); if (!data.isEmpty()) { @@ -611,7 +490,7 @@ QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pData return ret; } -class QWindowsMimeURI : public QNativeInterface::Private::QWindowsMime +class QWindowsMimeURI : public QWindowsMimeConverter { public: QWindowsMimeURI(); @@ -628,8 +507,8 @@ private: QWindowsMimeURI::QWindowsMimeURI() { - CF_INETURL_W = QWindowsMimeConverter::registerMimeType(QStringLiteral("UniformResourceLocatorW")); - CF_INETURL = QWindowsMimeConverter::registerMimeType(QStringLiteral("UniformResourceLocator")); + CF_INETURL_W = registerMimeType(u"UniformResourceLocatorW"_s); + CF_INETURL = registerMimeType(u"UniformResourceLocator"_s); } bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const @@ -670,7 +549,7 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat auto *f = reinterpret_cast<wchar_t *>(files); for (int i=0; i<fileNames.size(); i++) { const auto l = size_t(fileNames.at(i).length()); - memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort)); + memcpy(f, fileNames.at(i).data(), l * sizeof(ushort)); f += l; *f++ = 0; } @@ -682,8 +561,8 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat const auto urls = mimeData->urls(); QByteArray result; if (!urls.isEmpty()) { - QString url = urls.at(0).toString(); - result = QByteArray(reinterpret_cast<const char *>(url.utf16()), + const QString url = urls.at(0).toString(); + result = QByteArray(reinterpret_cast<const char *>(url.data()), url.length() * int(sizeof(ushort))); } result.append('\0'); @@ -712,7 +591,7 @@ QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const { QString format; if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) - format = QStringLiteral("text/uri-list"); + format = u"text/uri-list"_s; return format; } @@ -777,7 +656,7 @@ QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pD return QVariant(); } -class QWindowsMimeHtml : public QNativeInterface::Private::QWindowsMime +class QWindowsMimeHtml : public QWindowsMimeConverter { public: QWindowsMimeHtml(); @@ -798,7 +677,7 @@ private: QWindowsMimeHtml::QWindowsMimeHtml() { - CF_HTML = QWindowsMimeConverter::registerMimeType(QStringLiteral("HTML Format")); + CF_HTML = registerMimeType(u"HTML Format"_s); } QList<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const @@ -812,7 +691,7 @@ QList<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const { if (getCf(formatetc) == CF_HTML) - return QStringLiteral("text/html"); + return u"text/html"_s; return QString(); } @@ -915,7 +794,7 @@ bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeDa #ifndef QT_NO_IMAGEFORMAT_BMP -class QWindowsMimeImage : public QNativeInterface::Private::QWindowsMime +class QWindowsMimeImage : public QWindowsMimeConverter { public: QWindowsMimeImage(); @@ -929,6 +808,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; }; @@ -956,7 +836,7 @@ QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const { int cf = getCf(formatetc); if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG)) - return QStringLiteral("application/x-qt-image"); + return u"application/x-qt-image"_s; return QString(); } @@ -990,7 +870,7 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeD QByteArray ba; if (cf == CF_DIB) { if (img.format() > QImage::Format_ARGB32) - img = img.convertToFormat(QImage::Format_RGB32); + img = std::move(img).convertToFormat(QImage::Format_RGB32); const QByteArray ba = writeDib(img); if (!ba.isEmpty()) return setData(ba, pmedium); @@ -1003,22 +883,48 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeD } else { QDataStream s(&ba, QIODevice::WriteOnly); s.setByteOrder(QDataStream::LittleEndian);// Intel byte order #### - if (qt_write_dibv5(s, img)) + if (qt_write_dibv5(s, std::move(img))) return setData(ba, pmedium); } } 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); @@ -1027,6 +933,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")) { @@ -1035,6 +942,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); @@ -1046,7 +954,7 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject * } #endif -class QBuiltInMimes : public QNativeInterface::Private::QWindowsMime +class QBuiltInMimes : public QWindowsMimeConverter { public: QBuiltInMimes(); @@ -1067,10 +975,10 @@ private: }; QBuiltInMimes::QBuiltInMimes() -: QWindowsMime() +: QWindowsMimeConverter() { - outFormats.insert(QWindowsMimeConverter::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color")); - inFormats.insert(QWindowsMimeConverter::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color")); + outFormats.insert(registerMimeType(u"application/x-color"_s), u"application/x-color"_s); + inFormats.insert(registerMimeType(u"application/x-color"_s), u"application/x-color"_s); } bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const @@ -1167,7 +1075,7 @@ QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const } -class QLastResortMimes : public QNativeInterface::Private::QWindowsMime +class QLastResortMimes : public QWindowsMimeConverter { public: @@ -1195,25 +1103,25 @@ QLastResortMimes::QLastResortMimes() { //MIME Media-Types if (ianaTypes.isEmpty()) { - ianaTypes.append(QStringLiteral("application/")); - ianaTypes.append(QStringLiteral("audio/")); - ianaTypes.append(QStringLiteral("example/")); - ianaTypes.append(QStringLiteral("image/")); - ianaTypes.append(QStringLiteral("message/")); - ianaTypes.append(QStringLiteral("model/")); - ianaTypes.append(QStringLiteral("multipart/")); - ianaTypes.append(QStringLiteral("text/")); - ianaTypes.append(QStringLiteral("video/")); + ianaTypes.append(u"application/"_s); + ianaTypes.append(u"audio/"_s); + ianaTypes.append(u"example/"_s); + ianaTypes.append(u"image/"_s); + ianaTypes.append(u"message/"_s); + ianaTypes.append(u"model/"_s); + ianaTypes.append(u"multipart/"_s); + ianaTypes.append(u"text/"_s); + ianaTypes.append(u"video/"_s); } //Types handled by other classes if (excludeList.isEmpty()) { - excludeList.append(QStringLiteral("HTML Format")); - excludeList.append(QStringLiteral("UniformResourceLocator")); - excludeList.append(QStringLiteral("text/html")); - excludeList.append(QStringLiteral("text/plain")); - excludeList.append(QStringLiteral("text/uri-list")); - excludeList.append(QStringLiteral("application/x-qt-image")); - excludeList.append(QStringLiteral("application/x-color")); + excludeList.append(u"HTML Format"_s); + excludeList.append(u"UniformResourceLocator"_s); + excludeList.append(u"text/html"_s); + excludeList.append(u"text/plain"_s); + excludeList.append(u"text/uri-list"_s); + excludeList.append(u"application/x-qt-image"_s); + excludeList.append(u"application/x-color"_s); } } @@ -1251,7 +1159,7 @@ QList<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const auto mit = std::find(formats.begin(), formats.end(), mimeType); // register any other available formats if (mit == formats.end() && !excludeList.contains(mimeType, Qt::CaseInsensitive)) - mit = formats.insert(QWindowsMimeConverter::registerMimeType(mimeType), mimeType); + mit = formats.insert(registerMimeType(mimeType), mimeType); if (mit != formats.end()) formatetcs += setCf(mit.key()); @@ -1263,7 +1171,7 @@ static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\"" static bool isCustomMimeType(const QString &mimeType) { - return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive); + return mimeType.startsWith(QLatin1StringView(x_qt_windows_mime), Qt::CaseInsensitive); } static QString customMimeType(const QString &mimeType, int *lindex = nullptr) @@ -1295,7 +1203,7 @@ bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pD } // if it is not in there then register it and see if we can get it const auto mit = std::find(formats.cbegin(), formats.cend(), mimeType); - const int cf = mit != formats.cend() ? mit.key() : QWindowsMimeConverter::registerMimeType(mimeType); + const int cf = mit != formats.cend() ? mit.key() : registerMimeType(mimeType); return canGetData(cf, pDataObj); } @@ -1312,7 +1220,7 @@ QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *p data = getData(int(cf), pDataObj, lindex); } else { const auto mit = std::find(formats.cbegin(), formats.cend(), mimeType); - const int cf = mit != formats.cend() ? mit.key() : QWindowsMimeConverter::registerMimeType(mimeType); + const int cf = mit != formats.cend() ? mit.key() : registerMimeType(mimeType); data = getData(cf, pDataObj); } if (!data.isEmpty()) @@ -1327,7 +1235,7 @@ QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const if (!format.isEmpty()) return format; - const QString clipFormat = QWindowsMimeConverter::clipboardFormatName(getCf(formatetc)); + const QString clipFormat = QWindowsMimeRegistry::clipboardFormatName(getCf(formatetc)); if (!clipFormat.isEmpty()) { #if QT_CONFIG(draganddrop) if (QInternalMimeData::canReadData(clipFormat)) @@ -1345,7 +1253,7 @@ QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const } } if (!ianaType) - format = QLatin1String(x_qt_windows_mime) + clipFormat + u'"'; + format = QLatin1StringView(x_qt_windows_mime) + clipFormat + u'"'; else format = clipFormat; } @@ -1357,20 +1265,20 @@ QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const } /*! - \class QWindowsMimeConverter - \brief Manages the list of QWindowsMime instances. + \class QWindowsMimeRegistry + \brief Manages the list of QWindowsMimeConverter instances. \internal - \sa QWindowsMime + \sa QWindowsMimeConverter */ -QWindowsMimeConverter::QWindowsMimeConverter() = default; +QWindowsMimeRegistry::QWindowsMimeRegistry() = default; -QWindowsMimeConverter::~QWindowsMimeConverter() +QWindowsMimeRegistry::~QWindowsMimeRegistry() { qDeleteAll(m_mimes.begin(), m_mimes.begin() + m_internalMimeCount); } -QWindowsMimeConverter::QWindowsMime *QWindowsMimeConverter::converterToMime(const QString &mimeType, IDataObject *pDataObj) const +QWindowsMimeRegistry::QWindowsMimeConverter *QWindowsMimeRegistry::converterToMime(const QString &mimeType, IDataObject *pDataObj) const { ensureInitialized(); for (int i = m_mimes.size()-1; i >= 0; --i) { @@ -1380,9 +1288,9 @@ QWindowsMimeConverter::QWindowsMime *QWindowsMimeConverter::converterToMime(cons return nullptr; } -QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) const +QStringList QWindowsMimeRegistry::allMimesForFormats(IDataObject *pDataObj) const { - qCDebug(lcQpaMime) << "QWindowsMime::allMimesForFormats()"; + qCDebug(lcQpaMime) << "QWindowsMimeConverter::allMimesForFormats()"; ensureInitialized(); QStringList formats; LPENUMFORMATETC FAR fmtenum; @@ -1409,7 +1317,7 @@ QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) con return formats; } -QWindowsMimeConverter::QWindowsMime *QWindowsMimeConverter::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +QWindowsMimeRegistry::QWindowsMimeConverter *QWindowsMimeRegistry::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const { ensureInitialized(); qCDebug(lcQpaMime) << __FUNCTION__ << formatetc; @@ -1420,7 +1328,7 @@ QWindowsMimeConverter::QWindowsMime *QWindowsMimeConverter::converterFromMime(co return nullptr; } -QList<FORMATETC> QWindowsMimeConverter::allFormatsForMime(const QMimeData *mimeData) const +QList<FORMATETC> QWindowsMimeRegistry::allFormatsForMime(const QMimeData *mimeData) const { ensureInitialized(); QList<FORMATETC> formatics; @@ -1437,34 +1345,37 @@ QList<FORMATETC> QWindowsMimeConverter::allFormatsForMime(const QMimeData *mimeD return formatics; } -void QWindowsMimeConverter::ensureInitialized() const +void QWindowsMimeRegistry::ensureInitialized() const { - if (m_mimes.isEmpty()) { - m_mimes + if (m_internalMimeCount == 0) { + m_internalMimeCount = -1; // prevent reentrancy when types register themselves #ifndef QT_NO_IMAGEFORMAT_BMP - << new QWindowsMimeImage + (void)new QWindowsMimeImage; #endif //QT_NO_IMAGEFORMAT_BMP - << new QLastResortMimes - << new QWindowsMimeText << new QWindowsMimeURI - << new QWindowsMimeHtml << new QBuiltInMimes; + (void)new QLastResortMimes; + (void)new QWindowsMimeText; + (void)new QWindowsMimeURI; + (void)new QWindowsMimeHtml; + (void)new QBuiltInMimes; m_internalMimeCount = m_mimes.size(); + Q_ASSERT(m_internalMimeCount > 0); } } -QString QWindowsMimeConverter::clipboardFormatName(int cf) +QString QWindowsMimeRegistry::clipboardFormatName(int cf) { wchar_t buf[256] = {0}; return GetClipboardFormatName(UINT(cf), buf, 255) ? QString::fromWCharArray(buf) : QString(); } -QVariant QWindowsMimeConverter::convertToMime(const QStringList &mimeTypes, +QVariant QWindowsMimeRegistry::convertToMime(const QStringList &mimeTypes, IDataObject *pDataObj, QMetaType preferredType, QString *formatIn /* = 0 */) const { for (const QString &format : mimeTypes) { - if (const QWindowsMime *converter = converterToMime(format, pDataObj)) { + if (const QWindowsMimeConverter *converter = converterToMime(format, pDataObj)) { if (converter->canConvertToMime(format, pDataObj)) { const QVariant dataV = converter->convertToMime(format, pDataObj, preferredType); if (dataV.isValid()) { @@ -1481,7 +1392,7 @@ QVariant QWindowsMimeConverter::convertToMime(const QStringList &mimeTypes, return QVariant(); } -void QWindowsMimeConverter::registerMime(QWindowsMime *mime) +void QWindowsMimeRegistry::registerMime(QWindowsMimeConverter *mime) { ensureInitialized(); m_mimes.append(mime); @@ -1490,12 +1401,18 @@ void QWindowsMimeConverter::registerMime(QWindowsMime *mime) /*! Registers the MIME type \a mime, and returns an ID number identifying the format on Windows. + + A mime type \c {application/x-qt-windows-mime;value="WindowsType"} will be + registered as the clipboard format for \c WindowsType. */ -int QWindowsMimeConverter::registerMimeType(const QString &mime) +int QWindowsMimeRegistry::registerMimeType(const QString &mime) { - const UINT f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16())); - if (!f) - qErrnoWarning("QWindowsApplication::registerMimeType: Failed to register clipboard format"); + const QString mimeType = isCustomMimeType(mime) ? customMimeType(mime) : mime; + const UINT f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mimeType.utf16())); + if (!f) { + qErrnoWarning("QWindowsMimeRegistry::registerMimeType: Failed to register clipboard format " + "for %s", qPrintable(mime)); + } return int(f); } diff --git a/src/plugins/platforms/windows/qwindowsmimeregistry.h b/src/plugins/platforms/windows/qwindowsmimeregistry.h new file mode 100644 index 0000000000..a0f4b4c60a --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsmimeregistry.h @@ -0,0 +1,58 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSMIMEREGISTRY_H +#define QWINDOWSMIMEREGISTRY_H + + +#include <QtCore/qt_windows.h> + +#include <QtGui/qwindowsmimeconverter.h> +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_NAMESPACE + +class QDebug; +class QMimeData; + +class QWindowsMimeRegistry +{ + Q_DISABLE_COPY_MOVE(QWindowsMimeRegistry) +public: + using QWindowsMimeConverter = QWindowsMimeConverter; + + QWindowsMimeRegistry(); + ~QWindowsMimeRegistry(); + + QWindowsMimeConverter *converterToMime(const QString &mimeType, IDataObject *pDataObj) const; + QStringList allMimesForFormats(IDataObject *pDataObj) const; + QWindowsMimeConverter *converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + QList<FORMATETC> allFormatsForMime(const QMimeData *mimeData) const; + + // Convenience. + QVariant convertToMime(const QStringList &mimeTypes, IDataObject *pDataObj, QMetaType preferredType, + QString *format = nullptr) const; + + void registerMime(QWindowsMimeConverter *mime); + void unregisterMime(QWindowsMimeConverter *mime) { m_mimes.removeOne(mime); } + + static int registerMimeType(const QString &mime); + + static QString clipboardFormatName(int cf); + +private: + void ensureInitialized() const; + + mutable QList<QWindowsMimeConverter *> m_mimes; + mutable int m_internalMimeCount = 0; +}; + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug, const FORMATETC &); +QDebug operator<<(QDebug d, IDataObject *); +#endif + +QT_END_NAMESPACE + +#endif // QWINDOWSMIMEREGISTRY_H diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index 5165b4f2e4..9af9fba408 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsmousehandler.h" #include "qwindowskeymapper.h" @@ -52,7 +16,8 @@ #include <QtGui/qcursor.h> #include <QtCore/qdebug.h> -#include <QtCore/qscopedpointer.h> + +#include <memory> #include <windowsx.h> @@ -130,7 +95,9 @@ QWindowsMouseHandler::QWindowsMouseHandler() = default; const QPointingDevice *QWindowsMouseHandler::primaryMouse() { - static const auto result = QPointingDevice::primaryPointingDevice(); + static QPointer<const QPointingDevice> result; + if (!result) + result = QPointingDevice::primaryPointingDevice(); return result; } @@ -157,7 +124,7 @@ Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons() return result; } -static QPoint lastMouseMovePos; +Q_CONSTINIT static QPoint lastMouseMovePos; namespace { struct MouseEvent { @@ -258,8 +225,13 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, globalPosition = winEventPosition; clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition); } else { - clientPosition = winEventPosition; globalPosition = QWindowsGeometryHint::mapToGlobal(hwnd, winEventPosition); + auto targetHwnd = hwnd; + if (auto *pw = window->handle()) + targetHwnd = HWND(pw->winId()); + clientPosition = targetHwnd == hwnd + ? winEventPosition + : QWindowsGeometryHint::mapFromGlobal(targetHwnd, globalPosition); } // Windows sends a mouse move with no buttons pressed to signal "Enter" @@ -296,7 +268,8 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } } - const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers(); const MouseEvent mouseEvent = eventFromMsg(msg); Qt::MouseButtons buttons; @@ -314,19 +287,16 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, if (m_lastEventType == QEvent::NonClientAreaMouseButtonPress && (mouseEvent.type == QEvent::NonClientAreaMouseMove || mouseEvent.type == QEvent::MouseMove) && (m_lastEventButton & buttons) == 0) { - if (mouseEvent.type == QEvent::NonClientAreaMouseMove) { - QWindowSystemInterface::handleFrameStrutMouseEvent(window, device, clientPosition, globalPosition, buttons, m_lastEventButton, - QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source); - } else { - QWindowSystemInterface::handleMouseEvent(window, device, clientPosition, globalPosition, buttons, m_lastEventButton, - QEvent::MouseButtonRelease, keyModifiers, source); - } + auto releaseType = mouseEvent.type == QEvent::NonClientAreaMouseMove ? + QEvent::NonClientAreaMouseButtonRelease : QEvent::MouseButtonRelease; + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, clientPosition, globalPosition, buttons, m_lastEventButton, + releaseType, keyModifiers, source); } m_lastEventType = mouseEvent.type; m_lastEventButton = mouseEvent.button; if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) { - QWindowSystemInterface::handleFrameStrutMouseEvent(window, device, clientPosition, + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, clientPosition, globalPosition, buttons, mouseEvent.button, mouseEvent.type, keyModifiers, source); @@ -353,7 +323,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, auto *platformWindow = static_cast<QWindowsWindow *>(window->handle()); - // If the window was recently resized via mouse doubleclick on the frame or title bar, + // If the window was recently resized via mouse double-click on the frame or title bar, // we don't get WM_LBUTTONDOWN or WM_LBUTTONDBLCLK for the second click, // but we will get at least one WM_MOUSEMOVE with left button down and the WM_LBUTTONUP, // which will result undesired mouse press and release events. @@ -476,7 +446,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } if (!discardEvent && mouseEvent.type != QEvent::None) { - QWindowSystemInterface::handleMouseEvent(window, device, winEventPosition, globalPosition, buttons, + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, clientPosition, globalPosition, buttons, mouseEvent.button, mouseEvent.type, keyModifiers, source); } @@ -500,7 +470,7 @@ static bool isValidWheelReceiver(QWindow *candidate) return false; } -static void redirectWheelEvent(QWindow *window, const QPoint &globalPos, int delta, +static void redirectWheelEvent(QWindow *window, unsigned long timestamp, const QPoint &globalPos, int delta, Qt::Orientation orientation, Qt::KeyboardModifiers mods) { // Redirect wheel event to one of the following, in order of preference: @@ -521,6 +491,7 @@ static void redirectWheelEvent(QWindow *window, const QPoint &globalPos, int del if (handleEvent) { const QPoint point = (orientation == Qt::Vertical) ? QPoint(0, delta) : QPoint(delta, 0); QWindowSystemInterface::handleWheelEvent(receiver, + timestamp, QWindowsGeometryHint::mapFromGlobal(receiver, globalPos), globalPos, QPoint(), point, mods); } @@ -549,7 +520,7 @@ bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND, delta = -delta; const QPoint globalPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); - redirectWheelEvent(window, globalPos, delta, orientation, mods); + redirectWheelEvent(window, msg.time, globalPos, delta, orientation, mods); return true; } @@ -580,7 +551,7 @@ bool QWindowsMouseHandler::translateScrollEvent(QWindow *window, HWND, return false; } - redirectWheelEvent(window, QCursor::pos(), delta, Qt::Horizontal, Qt::NoModifier); + redirectWheelEvent(window, msg.time, QCursor::pos(), delta, Qt::Horizontal, Qt::NoModifier); return true; } @@ -606,15 +577,14 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, const QRect screenGeometry = screen->geometry(); const int winTouchPointCount = int(msg.wParam); - QScopedArrayPointer<TOUCHINPUT> winTouchInputs(new TOUCHINPUT[winTouchPointCount]); - memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * size_t(winTouchPointCount)); + const auto winTouchInputs = std::make_unique<TOUCHINPUT[]>(winTouchPointCount); QTouchPointList touchPoints; touchPoints.reserve(winTouchPointCount); QEventPoint::States allStates; GetTouchInputInfo(reinterpret_cast<HTOUCHINPUT>(msg.lParam), - UINT(msg.wParam), winTouchInputs.data(), sizeof(TOUCHINPUT)); + UINT(msg.wParam), winTouchInputs.get(), sizeof(TOUCHINPUT)); for (int i = 0; i < winTouchPointCount; ++i) { const TOUCHINPUT &winTouchInput = winTouchInputs[i]; int id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1); @@ -661,10 +631,12 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, if (allStates == QEventPoint::State::Released) m_touchInputIDToTouchPointID.clear(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); QWindowSystemInterface::handleTouchEvent(window, + msg.time, m_touchDevice.data(), touchPoints, - QWindowsKeyMapper::queryKeyboardModifiers()); + keyMapper->queryKeyboardModifiers()); return true; } diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.h b/src/plugins/platforms/windows/qwindowsmousehandler.h index 6fd8061ddd..7fde349f58 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.h +++ b/src/plugins/platforms/windows/qwindowsmousehandler.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSMOUSEHANDLER_H #define QWINDOWSMOUSEHANDLER_H diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp index a971cdae0b..e709123097 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsnativeinterface.h" #include "qwindowswindow.h" diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.h b/src/plugins/platforms/windows/qwindowsnativeinterface.h index 8ef14e8603..a123c2b242 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.h +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSNATIVEINTERFACE_H #define QWINDOWSNATIVEINTERFACE_H diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp index c040137a6d..c01f716054 100644 --- a/src/plugins/platforms/windows/qwindowsole.cpp +++ b/src/plugins/platforms/windows/qwindowsole.cpp @@ -1,44 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsole.h" -#include "qwindowsmime.h" +#include "qwindowsmimeregistry.h" #include "qwindowscontext.h" \ #include <QtGui/qevent.h> @@ -102,7 +66,7 @@ QWindowsOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) HRESULT hr = ResultFromScode(DATA_E_FORMATETC); if (data) { - const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); if (auto converter = mc.converterFromMime(*pformatetc, data)) if (converter->convertFromMime(*pformatetc, data, pmedium)) hr = ResultFromScode(S_OK); @@ -129,7 +93,7 @@ QWindowsOleDataObject::QueryGetData(LPFORMATETC pformatetc) qCDebug(lcQpaMime) << __FUNCTION__; if (data) { - const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); hr = mc.converterFromMime(*pformatetc, data) ? ResultFromScode(S_OK) : ResultFromScode(S_FALSE); } @@ -180,7 +144,7 @@ QWindowsOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppe QList<FORMATETC> fmtetcs; if (dwDirection == DATADIR_GET) { - QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); fmtetcs = mc.allFormatsForMime(data); } else { FORMATETC formatetc; diff --git a/src/plugins/platforms/windows/qwindowsole.h b/src/plugins/platforms/windows/qwindowsole.h index c556a3fb9c..016f9dd04c 100644 --- a/src/plugins/platforms/windows/qwindowsole.h +++ b/src/plugins/platforms/windows/qwindowsole.h @@ -1,51 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSOLE_H #define QWINDOWSOLE_H -#include "qwindowscombase.h" #include <QtCore/qt_windows.h> #include <QtCore/qlist.h> #include <QtCore/qmap.h> #include <QtCore/qpointer.h> +#include <QtCore/private/qcomobject_p.h> #include <objidl.h> @@ -54,7 +18,7 @@ QT_BEGIN_NAMESPACE class QMimeData; class QWindow; -class QWindowsOleDataObject : public QWindowsComBase<IDataObject> +class QWindowsOleDataObject : public QComObject<IDataObject> { public: explicit QWindowsOleDataObject(QMimeData *mimeData); @@ -83,7 +47,7 @@ private: DWORD performedEffect = DROPEFFECT_NONE; }; -class QWindowsOleEnumFmtEtc : public QWindowsComBase<IEnumFORMATETC> +class QWindowsOleEnumFmtEtc : public QComObject<IEnumFORMATETC> { public: explicit QWindowsOleEnumFmtEtc(const QList<FORMATETC> &fmtetcs); diff --git a/src/plugins/platforms/windows/qwindowsopenglcontext.h b/src/plugins/platforms/windows/qwindowsopenglcontext.h index 78ff23669c..3fefe0ad91 100644 --- a/src/plugins/platforms/windows/qwindowsopenglcontext.h +++ b/src/plugins/platforms/windows/qwindowsopenglcontext.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSOPENGLCONTEXT_H #define QWINDOWSOPENGLCONTEXT_H diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp index 0b1af47a65..6a790bcc1b 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.cpp +++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp @@ -1,46 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsopengltester.h" #include "qwindowscontext.h" #include <QtCore/qvariant.h> +#include <QtCore/qmap.h> #include <QtCore/qdebug.h> #include <QtCore/qtextstream.h> #include <QtCore/qcoreapplication.h> @@ -49,13 +14,14 @@ #include <QtCore/qstandardpaths.h> #include <QtCore/qlibraryinfo.h> #include <QtCore/qhash.h> +#include <private/qsystemlibrary_p.h> +#include <QtGui/qtgui-config.h> #ifndef QT_NO_OPENGL #include <private/qopengl_p.h> #endif #include <QtCore/qt_windows.h> -#include <private/qsystemlibrary_p.h> #include <d3d9.h> QT_BEGIN_NAMESPACE @@ -94,19 +60,12 @@ public: bool retrieveAdapterIdentifier(UINT n, D3DADAPTER_IDENTIFIER9 *adapterIdentifier) const; private: - QSystemLibrary m_d3d9lib; IDirect3D9 *m_direct3D9 = nullptr; }; -QDirect3D9Handle::QDirect3D9Handle() : - m_d3d9lib(QStringLiteral("d3d9")) +QDirect3D9Handle::QDirect3D9Handle() { - using PtrDirect3DCreate9 = IDirect3D9 *(WINAPI *)(UINT); - - if (m_d3d9lib.load()) { - if (auto direct3DCreate9 = (PtrDirect3DCreate9)m_d3d9lib.resolve("Direct3DCreate9")) - m_direct3D9 = direct3DCreate9(D3D_SDK_VERSION); - } + m_direct3D9 = Direct3DCreate9(D3D_SDK_VERSION); } QDirect3D9Handle::~QDirect3D9Handle() @@ -224,13 +183,13 @@ QVariant GpuDescription::toVariant() const result.insert(QStringLiteral("deviceId"), QVariant(deviceId)); result.insert(QStringLiteral("subSysId"),QVariant(subSysId)); result.insert(QStringLiteral("revision"), QVariant(revision)); - result.insert(QStringLiteral("driver"), QVariant(QLatin1String(driverName))); + result.insert(QStringLiteral("driver"), QVariant(QLatin1StringView(driverName))); result.insert(QStringLiteral("driverProduct"), QVariant(driverVersion.segmentAt(0))); result.insert(QStringLiteral("driverVersion"), QVariant(driverVersion.segmentAt(1))); result.insert(QStringLiteral("driverSubVersion"), QVariant(driverVersion.segmentAt(2))); result.insert(QStringLiteral("driverBuild"), QVariant(driverVersion.segmentAt(3))); result.insert(QStringLiteral("driverVersionString"), driverVersion.toString()); - result.insert(QStringLiteral("description"), QVariant(QLatin1String(description))); + result.insert(QStringLiteral("description"), QVariant(QLatin1StringView(description))); result.insert(QStringLiteral("printable"), QVariant(toString())); return result; } @@ -358,7 +317,7 @@ bool QWindowsOpenGLTester::testDesktopGL() // Test #1: Load opengl32.dll and try to resolve an OpenGL 2 function. // This will typically fail on systems that do not have a real OpenGL driver. - lib = LoadLibraryA("opengl32.dll"); + lib = QSystemLibrary::load(L"opengl32"); if (lib) { CreateContext = reinterpret_cast<CreateContextType>( reinterpret_cast<QFunctionPointer>(::GetProcAddress(lib, "wglCreateContext"))); diff --git a/src/plugins/platforms/windows/qwindowsopengltester.h b/src/plugins/platforms/windows/qwindowsopengltester.h index 9091949699..abda0c2dc1 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.h +++ b/src/plugins/platforms/windows/qwindowsopengltester.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSOPENGLTESTER_H #define QWINDOWSOPENGLTESTER_H diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index 24d85c76e6..71c7217671 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -1,48 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#if defined(WINVER) && WINVER < 0x0603 -# undef WINVER -#endif -#if !defined(WINVER) -# define WINVER 0x0603 // Enable pointer functions for MinGW -#endif +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtCore/qt_windows.h> #include "qwindowspointerhandler.h" #include "qwindowsmousehandler.h" @@ -62,7 +21,6 @@ #include <QtGui/private/qguiapplication_p.h> #include <QtCore/qvarlengtharray.h> #include <QtCore/qloggingcategory.h> -#include <QtCore/qoperatingsystemversion.h> #include <QtCore/qqueue.h> #include <algorithm> @@ -90,7 +48,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q *result = 0; const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); - if (!QWindowsContext::user32dll.getPointerType(pointerId, &m_pointerType)) { + if (!GetPointerType(pointerId, &m_pointerType)) { qWarning() << "GetPointerType() failed:" << qt_error_string(); return false; } @@ -104,12 +62,12 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q } case QT_PT_TOUCH: { quint32 pointerCount = 0; - if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) { + if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) { qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string(); return false; } QVarLengthArray<POINTER_TOUCH_INFO, 10> touchInfo(pointerCount); - if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) { + if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) { qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string(); return false; } @@ -122,10 +80,10 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q // dispatch any skipped frames if event compression is disabled by the app if (historyCount > 1 && !QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) { touchInfo.resize(pointerCount * historyCount); - if (!QWindowsContext::user32dll.getPointerFrameTouchInfoHistory(pointerId, - &historyCount, - &pointerCount, - touchInfo.data())) { + if (!GetPointerFrameTouchInfoHistory(pointerId, + &historyCount, + &pointerCount, + touchInfo.data())) { qWarning() << "GetPointerFrameTouchInfoHistory() failed:" << qt_error_string(); return false; } @@ -143,7 +101,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q } case QT_PT_PEN: { POINTER_PEN_INFO penInfo; - if (!QWindowsContext::user32dll.getPointerPenInfo(pointerId, &penInfo)) { + if (!GetPointerPenInfo(pointerId, &penInfo)) { qWarning() << "GetPointerPenInfo() failed:" << qt_error_string(); return false; } @@ -155,9 +113,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q || !QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents))) { QVarLengthArray<POINTER_PEN_INFO, 10> penInfoHistory(historyCount); - if (!QWindowsContext::user32dll.getPointerPenInfoHistory(pointerId, - &historyCount, - penInfoHistory.data())) { + if (!GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) { qWarning() << "GetPointerPenInfoHistory() failed:" << qt_error_string(); return false; } @@ -463,6 +419,8 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, { Q_UNUSED(hwnd); + auto *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo); + if (et & QtWindows::NonClientEventFlag) return false; // Let DefWindowProc() handle Non Client messages. @@ -470,12 +428,24 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, return false; if (msg.message == WM_POINTERCAPTURECHANGED) { - QWindowSystemInterface::handleTouchCancelEvent(window, m_touchDevice.data(), - QWindowsKeyMapper::queryKeyboardModifiers()); - m_lastTouchPositions.clear(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + QWindowSystemInterface::handleTouchCancelEvent(window, msg.time, m_touchDevice.data(), + keyMapper->queryKeyboardModifiers()); + m_lastTouchPoints.clear(); return true; } + if (msg.message == WM_POINTERLEAVE) { + for (quint32 i = 0; i < count; ++i) { + const quint32 pointerId = touchInfo[i].pointerInfo.pointerId; + int id = m_touchInputIDToTouchPointID.value(pointerId, -1); + if (id != -1) + m_lastTouchPoints.remove(id); + } + // Send LeaveEvent to reset hover when the last finger leaves the touch screen (QTBUG-62912) + QWindowSystemInterface::handleEnterLeaveEvent(nullptr, window); + } + // Only handle down/up/update, ignore others like WM_POINTERENTER, WM_POINTERLEAVE, etc. if (msg.message > WM_POINTERUP) return false; @@ -486,8 +456,6 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, if (!screen) return false; - auto *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo); - const QRect screenGeometry = screen->geometry(); QList<QWindowSystemInterface::TouchPoint> touchPoints; @@ -499,6 +467,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, << " count=" << Qt::dec << count; QEventPoint::States allStates; + QSet<int> inputIds; for (quint32 i = 0; i < count; ++i) { if (QWindowsContext::verbose > 1) @@ -511,14 +480,17 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, const quint32 pointerId = touchInfo[i].pointerInfo.pointerId; int id = m_touchInputIDToTouchPointID.value(pointerId, -1); if (id == -1) { + // Start tracking after fingers touch the screen. Ignore bogus updates after touch is released. + if ((touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) == 0) + continue; id = m_touchInputIDToTouchPointID.size(); m_touchInputIDToTouchPointID.insert(pointerId, id); } touchPoint.id = id; touchPoint.pressure = (touchInfo[i].touchMask & TOUCH_MASK_PRESSURE) ? touchInfo[i].pressure / 1024.0 : 1.0; - if (m_lastTouchPositions.contains(touchPoint.id)) - touchPoint.normalPosition = m_lastTouchPositions.value(touchPoint.id); + if (m_lastTouchPoints.contains(touchPoint.id)) + touchPoint.normalPosition = m_lastTouchPoints.value(touchPoint.id).normalPosition; const QPointF screenPos = QPointF(touchInfo[i].pointerInfo.ptPixelLocation.x, touchInfo[i].pointerInfo.ptPixelLocation.y); @@ -534,28 +506,43 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) { touchPoint.state = QEventPoint::State::Pressed; - m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition); + m_lastTouchPoints.insert(touchPoint.id, touchPoint); } else if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_UP) { touchPoint.state = QEventPoint::State::Released; - m_lastTouchPositions.remove(touchPoint.id); + m_lastTouchPoints.remove(touchPoint.id); } else { touchPoint.state = stationaryTouchPoint ? QEventPoint::State::Stationary : QEventPoint::State::Updated; - m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition); + m_lastTouchPoints.insert(touchPoint.id, touchPoint); } allStates |= touchPoint.state; touchPoints.append(touchPoint); + inputIds.insert(touchPoint.id); // Avoid getting repeated messages for this frame if there are multiple pointerIds - QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId); + SkipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId); } + // 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 : std::as_const(m_lastTouchPoints)) { + if (!inputIds.contains(tp.id)) { + tp.state = QEventPoint::State::Stationary; + allStates |= tp.state; + touchPoints.append(tp); + } + } + + if (touchPoints.count() == 0) + return false; + // all touch points released, forget the ids we've seen. if (allStates == QEventPoint::State::Released) m_touchInputIDToTouchPointID.clear(); - QWindowSystemInterface::handleTouchEvent(window, m_touchDevice.data(), touchPoints, - QWindowsKeyMapper::queryKeyboardModifiers()); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + QWindowSystemInterface::handleTouchEvent(window, msg.time, m_touchDevice.data(), touchPoints, + keyMapper->queryKeyboardModifiers()); return false; // Allow mouse messages to be generated. } @@ -580,7 +567,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin auto *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo); RECT pRect, dRect; - if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect)) + if (!GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect)) return false; const auto systemId = (qint64)penInfo->pointerInfo.sourceDevice; @@ -652,7 +639,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin switch (msg.message) { case WM_POINTERENTER: { - QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, device.data(), true); + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, msg.time, device.data(), true); m_windowUnderPointer = window; // The local coordinates may fall outside the window. // Wait until the next update to send the enter event. @@ -665,7 +652,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin m_windowUnderPointer = nullptr; m_currentWindow = nullptr; } - QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, device.data(), false); + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, msg.time, device.data(), false); break; case WM_POINTERDOWN: case WM_POINTERUP: @@ -688,9 +675,10 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin wumPlatformWindow->applyCursor(); } } - const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers(); - QWindowSystemInterface::handleTabletEvent(target, device.data(), + QWindowSystemInterface::handleTabletEvent(target, msg.time, device.data(), localPos, hiResGlobalPos, mouseButtons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, keyModifiers); @@ -741,7 +729,7 @@ bool QWindowsPointerHandler::translateMouseWheelEvent(QWindow *window, QPoint localPos = QWindowsGeometryHint::mapFromGlobal(receiver, globalPos); - QWindowSystemInterface::handleWheelEvent(receiver, localPos, globalPos, QPoint(), angleDelta, keyModifiers); + QWindowSystemInterface::handleWheelEvent(receiver, msg.time, localPos, globalPos, QPoint(), angleDelta, keyModifiers); return true; } @@ -754,25 +742,31 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, { *result = 0; - QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); - if ((et & QtWindows::NonClientEventFlag) == 0 && QWindowsBaseWindow::isRtlLayout(hwnd)) { - RECT clientArea; - GetClientRect(hwnd, &clientArea); - eventPos.setX(clientArea.right - eventPos.x()); - } - QPoint localPos; QPoint globalPos; + QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); if ((et == QtWindows::MouseWheelEvent) || (et & QtWindows::NonClientEventFlag)) { globalPos = eventPos; localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, eventPos); } else { - localPos = eventPos; + if (QWindowsBaseWindow::isRtlLayout(hwnd)) { + RECT clientArea; + GetClientRect(hwnd, &clientArea); + eventPos.setX(clientArea.right - eventPos.x()); + } + globalPos = QWindowsGeometryHint::mapToGlobal(hwnd, eventPos); + auto targetHwnd = hwnd; + if (auto *pw = window->handle()) + targetHwnd = HWND(pw->winId()); + localPos = targetHwnd == hwnd + ? eventPos + : QWindowsGeometryHint::mapFromGlobal(targetHwnd, globalPos); } - const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers(); QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos); if (et == QtWindows::MouseWheelEvent) @@ -784,7 +778,7 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, // X11 and macOS. bool discardEvent = false; if (msg.message == WM_MOUSEMOVE) { - static QPoint lastMouseMovePos; + Q_CONSTINIT static QPoint lastMouseMovePos; if (msg.wParam == 0 && (m_windowUnderPointer.isNull() || globalPos == lastMouseMovePos)) discardEvent = true; lastMouseMovePos = globalPos; @@ -808,8 +802,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; } @@ -832,19 +826,16 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, if (m_lastEventType == QEvent::NonClientAreaMouseButtonPress && (mouseEvent.type == QEvent::NonClientAreaMouseMove || mouseEvent.type == QEvent::MouseMove) && (m_lastEventButton & mouseButtons) == 0) { - if (mouseEvent.type == QEvent::NonClientAreaMouseMove) { - QWindowSystemInterface::handleFrameStrutMouseEvent(window, device, localPos, globalPos, mouseButtons, m_lastEventButton, - QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source); - } else { - QWindowSystemInterface::handleMouseEvent(window, device, localPos, globalPos, mouseButtons, m_lastEventButton, - QEvent::MouseButtonRelease, keyModifiers, source); - } + auto releaseType = mouseEvent.type == QEvent::NonClientAreaMouseMove ? + QEvent::NonClientAreaMouseButtonRelease : QEvent::MouseButtonRelease; + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons, m_lastEventButton, + releaseType, keyModifiers, source); } m_lastEventType = mouseEvent.type; m_lastEventButton = mouseEvent.button; if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) { - QWindowSystemInterface::handleFrameStrutMouseEvent(window, device, localPos, globalPos, mouseButtons, + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons, mouseEvent.button, mouseEvent.type, keyModifiers, source); return false; // Allow further event processing } @@ -864,7 +855,7 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, handleEnterLeave(window, currentWindowUnderPointer, globalPos); if (!discardEvent && mouseEvent.type != QEvent::None) { - QWindowSystemInterface::handleMouseEvent(window, device, localPos, globalPos, mouseButtons, + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons, mouseEvent.button, mouseEvent.type, keyModifiers, source); } diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.h b/src/plugins/platforms/windows/qwindowspointerhandler.h index 5b6179b7fa..b64a8c146a 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.h +++ b/src/plugins/platforms/windows/qwindowspointerhandler.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSPOINTERHANDLER_H #define QWINDOWSPOINTERHANDLER_H @@ -48,6 +12,7 @@ #include <QtCore/qsharedpointer.h> #include <QtCore/qhash.h> #include <QtGui/qevent.h> +#include <qpa/qwindowsysteminterface.h> QT_BEGIN_NAMESPACE @@ -88,7 +53,7 @@ private: QList<QPointingDevicePtr> m_tabletDevices; QPointingDevicePtr m_activeTabletDevice; #endif - QHash<int, QPointF> m_lastTouchPositions; + QHash<int, QWindowSystemInterface::TouchPoint> m_lastTouchPoints; QHash<DWORD, int> m_touchInputIDToTouchPointID; QPointer<QWindow> m_windowUnderPointer; QPointer<QWindow> m_currentWindow; diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 2544dd6200..a50f9fd4b0 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsscreen.h" #include "qwindowscontext.h" @@ -50,14 +14,28 @@ #include <QtGui/qpixmap.h> #include <QtGui/qguiapplication.h> #include <qpa/qwindowsysteminterface.h> +#include <QtCore/private/qsystemerror_p.h> +#include <QtGui/private/qedidparser_p.h> #include <private/qhighdpiscaling_p.h> #include <private/qwindowsfontdatabasebase_p.h> +#include <private/qpixmap_win_p.h> +#include <private/quniquehandle_p.h> + #include <QtGui/qscreen.h> #include <QtCore/qdebug.h> +#include <memory> +#include <type_traits> + +#include <cfgmgr32.h> +#include <setupapi.h> +#include <shellscalingapi.h> + QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + static inline QDpi deviceDPI(HDC hdc) { return QDpi(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY)); @@ -65,16 +43,14 @@ static inline QDpi deviceDPI(HDC hdc) static inline QDpi monitorDPI(HMONITOR hMonitor) { - if (QWindowsContext::shcoredll.isValid()) { - UINT dpiX; - UINT dpiY; - if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY))) - return QDpi(dpiX, dpiY); - } + UINT dpiX; + UINT dpiY; + if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) + return QDpi(dpiX, dpiY); return {0, 0}; } -static bool getPathInfo(const MONITORINFOEX &viewInfo, DISPLAYCONFIG_PATH_INFO *pathInfo) +static std::vector<DISPLAYCONFIG_PATH_INFO> getPathInfo(const MONITORINFOEX &viewInfo) { // We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO. std::vector<DISPLAYCONFIG_PATH_INFO> pathInfos; @@ -90,7 +66,7 @@ static bool getPathInfo(const MONITORINFOEX &viewInfo, DISPLAYCONFIG_PATH_INFO * // look up the needed buffer sizes. if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements, &numModeInfoArrayElements) != ERROR_SUCCESS) { - return false; + return {}; } pathInfos.resize(numPathArrayElements); modeInfos.resize(numModeInfoArrayElements); @@ -99,24 +75,25 @@ static bool getPathInfo(const MONITORINFOEX &viewInfo, DISPLAYCONFIG_PATH_INFO * } while (result == ERROR_INSUFFICIENT_BUFFER); if (result != ERROR_SUCCESS) - return false; - - // Find path matching monitor name - for (uint32_t p = 0; p < numPathArrayElements; p++) { - DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName; - deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; - deviceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME); - deviceName.header.adapterId = pathInfos[p].sourceInfo.adapterId; - deviceName.header.id = pathInfos[p].sourceInfo.id; - if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) { - if (wcscmp(viewInfo.szDevice, deviceName.viewGdiDeviceName) == 0) { - *pathInfo = pathInfos[p]; + return {}; + + // Find paths matching monitor name + auto discardThese = + std::remove_if(pathInfos.begin(), pathInfos.end(), [&](const auto &path) -> bool { + DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName; + deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; + deviceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME); + deviceName.header.adapterId = path.sourceInfo.adapterId; + deviceName.header.id = path.sourceInfo.id; + if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) { + return wcscmp(viewInfo.szDevice, deviceName.viewGdiDeviceName) != 0; + } return true; - } - } - } + }); + + pathInfos.erase(discardThese, pathInfos.end()); - return false; + return pathInfos; } #if 0 @@ -140,6 +117,149 @@ static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo) using WindowsScreenDataList = QList<QWindowsScreenData>; +namespace { + +struct DiRegKeyHandleTraits +{ + using Type = HKEY; + static Type invalidValue() + { + // The setupapi.h functions return INVALID_HANDLE_VALUE when failing to open a registry key + return reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE); + } + static bool close(Type handle) { return RegCloseKey(handle) == ERROR_SUCCESS; } +}; + +using DiRegKeyHandle = QUniqueHandle<DiRegKeyHandleTraits>; + +} + +static void setMonitorDataFromSetupApi(QWindowsScreenData &data, + const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup) +{ + if (pathGroup.empty()) { + return; + } + + // The only property shared among monitors in a clone group is deviceName + { + DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {}; + deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; + deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME); + // The first element in the clone group is the main monitor. + deviceName.header.adapterId = pathGroup[0].targetInfo.adapterId; + deviceName.header.id = pathGroup[0].targetInfo.id; + if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) { + data.devicePath = QString::fromWCharArray(deviceName.monitorDevicePath); + } else { + qCWarning(lcQpaScreen) + << u"Unable to get device information for %1:"_s.arg(pathGroup[0].targetInfo.id) + << QSystemError::windowsString(); + } + } + + // The rest must be concatenated into the resulting property + QStringList names; + QStringList manufacturers; + QStringList models; + QStringList serialNumbers; + + for (const auto &path : pathGroup) { + DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {}; + deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; + deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME); + deviceName.header.adapterId = path.targetInfo.adapterId; + deviceName.header.id = path.targetInfo.id; + if (DisplayConfigGetDeviceInfo(&deviceName.header) != ERROR_SUCCESS) { + qCWarning(lcQpaScreen) + << u"Unable to get device information for %1:"_s.arg(path.targetInfo.id) + << QSystemError::windowsString(); + continue; + } + + // https://learn.microsoft.com/en-us/windows-hardware/drivers/install/guid-devinterface-monitor + constexpr GUID GUID_DEVINTERFACE_MONITOR = { + 0xe6f07b5f, 0xee97, 0x4a90, { 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 } + }; + const HDEVINFO devInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_MONITOR, nullptr, nullptr, + DIGCF_DEVICEINTERFACE); + + SP_DEVICE_INTERFACE_DATA deviceInterfaceData{}; + deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); + + if (!SetupDiOpenDeviceInterfaceW(devInfo, deviceName.monitorDevicePath, DIODI_NO_ADD, + &deviceInterfaceData)) { + qCWarning(lcQpaScreen) + << u"Unable to open monitor interface to %1:"_s.arg(data.deviceName) + << QSystemError::windowsString(); + continue; + } + + DWORD requiredSize{ 0 }; + if (SetupDiGetDeviceInterfaceDetailW(devInfo, &deviceInterfaceData, nullptr, 0, + &requiredSize, nullptr) + || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + continue; + } + + const std::unique_ptr<std::byte[]> storage(new std::byte[requiredSize]); + auto *devicePath = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_W *>(storage.get()); + devicePath->cbSize = sizeof(std::remove_pointer_t<decltype(devicePath)>); + SP_DEVINFO_DATA deviceInfoData{}; + deviceInfoData.cbSize = sizeof(deviceInfoData); + if (!SetupDiGetDeviceInterfaceDetailW(devInfo, &deviceInterfaceData, devicePath, + requiredSize, nullptr, &deviceInfoData)) { + qCDebug(lcQpaScreen) << u"Unable to get monitor metadata for %1:"_s.arg(data.deviceName) + << QSystemError::windowsString(); + continue; + } + + const DiRegKeyHandle edidRegistryKey{ SetupDiOpenDevRegKey( + devInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) }; + + if (!edidRegistryKey.isValid()) + continue; + + DWORD edidDataSize{ 0 }; + if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr, nullptr, + &edidDataSize) + != ERROR_SUCCESS) { + continue; + } + + QByteArray edidData; + edidData.resize(edidDataSize); + + if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr, + reinterpret_cast<unsigned char *>(edidData.data()), &edidDataSize) + != ERROR_SUCCESS) { + qCDebug(lcQpaScreen) << u"Unable to get EDID from the Registry for %1:"_s.arg( + data.deviceName) + << QSystemError::windowsString(); + continue; + } + + QEdidParser edid; + + if (!edid.parse(edidData)) { + qCDebug(lcQpaScreen) << "Invalid EDID blob for" << data.deviceName; + continue; + } + + // We skip edid.identifier because it is unreliable, and a better option + // is already available through DisplayConfigGetDeviceInfo (see below). + names << QString::fromWCharArray(deviceName.monitorFriendlyDeviceName); + manufacturers << edid.manufacturer; + models << edid.model; + serialNumbers << edid.serialNumber; + } + + data.name = names.join(u"|"_s); + data.manufacturer = manufacturers.join(u"|"_s); + data.model = models.join(u"|"_s); + data.serialNumber = serialNumbers.join(u"|"_s); +} + static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) { MONITORINFOEX info; @@ -151,8 +271,14 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) data->hMonitor = hMonitor; data->geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1)); data->availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1)); - data->name = QString::fromWCharArray(info.szDevice); - if (data->name == u"WinDisc") { + data->deviceName = QString::fromWCharArray(info.szDevice); + const auto pathGroup = getPathInfo(info); + if (!pathGroup.empty()) { + setMonitorDataFromSetupApi(*data, pathGroup); + } + if (data->name.isEmpty()) + data->name = data->deviceName; + if (data->deviceName == u"WinDisc") { data->flags |= QWindowsScreenData::LockScreen; } else { if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) { @@ -167,15 +293,16 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) DeleteDC(hdc); } else { qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.", - __FUNCTION__, qPrintable(QString::fromWCharArray(info.szDevice)), + __FUNCTION__, qPrintable(data->deviceName), data->dpi.first); } // CreateDC() failed } // not lock screen // ### We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO, // if we are going to use DISPLAYCONFIG lookups more. - DISPLAYCONFIG_PATH_INFO pathInfo = {}; - if (getPathInfo(info, &pathInfo)) { + if (!pathGroup.empty()) { + // The first element in the clone group is the main monitor. + const auto &pathInfo = pathGroup[0]; switch (pathInfo.targetInfo.rotation) { case DISPLAYCONFIG_ROTATION_IDENTITY: data->orientation = Qt::LandscapeOrientation; @@ -204,11 +331,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; } @@ -218,6 +342,16 @@ BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM QWindowsScreenData data; if (monitorData(hMonitor, &data)) { auto *result = reinterpret_cast<WindowsScreenDataList *>(p); + auto it = std::find_if(result->rbegin(), result->rend(), + [&data](QWindowsScreenData i){ return i.name == data.name; }); + if (it != result->rend()) { + int previousIndex = 1; + if (it->deviceIndex.has_value()) + previousIndex = it->deviceIndex.value(); + else + (*it).deviceIndex = 1; + data.deviceIndex = previousIndex + 1; + } // QWindowSystemInterface::handleScreenAdded() documentation specifies that first // added screen will be the primary screen, so order accordingly. // Note that the side effect of this policy is that there is no way to change primary @@ -244,14 +378,14 @@ static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d) QDebugStateSaver saver(dbg); dbg.nospace(); dbg.noquote(); - dbg << "Screen \"" << d.name << "\" " - << d.geometry.width() << 'x' << d.geometry.height() << '+' << d.geometry.x() << '+' << d.geometry.y() - << " avail: " - << d.availableGeometry.width() << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+' << d.availableGeometry.y() - << " physical: " << d.physicalSizeMM.width() << 'x' << d.physicalSizeMM.height() - << " DPI: " << d.dpi.first << 'x' << d.dpi.second << " Depth: " << d.depth - << " Format: " << d.format - << " hMonitor: " << d.hMonitor; + dbg << "Screen \"" << d.name << "\" " << d.geometry.width() << 'x' << d.geometry.height() << '+' + << d.geometry.x() << '+' << d.geometry.y() << " avail: " << d.availableGeometry.width() + << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+' + << d.availableGeometry.y() << " physical: " << d.physicalSizeMM.width() << 'x' + << d.physicalSizeMM.height() << " DPI: " << d.dpi.first << 'x' << d.dpi.second + << " Depth: " << d.depth << " Format: " << d.format << " hMonitor: " << d.hMonitor + << " device name: " << d.deviceName << " manufacturer: " << d.manufacturer + << " model: " << d.model << " serial number: " << d.serialNumber; if (d.flags & QWindowsScreenData::PrimaryScreen) dbg << " primary"; if (d.flags & QWindowsScreenData::VirtualDesktop) @@ -277,7 +411,12 @@ QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) : { } -Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0); +QString QWindowsScreen::name() const +{ + return m_data.deviceIndex.has_value() + ? (u"%1 (%2)"_s).arg(m_data.name, QString::number(m_data.deviceIndex.value())) + : m_data.name; +} QPixmap QWindowsScreen::grabWindow(WId window, int xIn, int yIn, int width, int height) const { @@ -337,7 +476,7 @@ QWindow *QWindowsScreen::topLevelAt(const QPoint &point) const if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE)) result = QWindowsWindow::topLevelOf(child); if (QWindowsContext::verbose > 1) - qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result; + qCDebug(lcQpaScreen) <<__FUNCTION__ << point << result; return result; } @@ -348,7 +487,7 @@ QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags) findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags)) result = bw->window(); if (QWindowsContext::verbose > 1) - qCDebug(lcQpaWindows) <<__FUNCTION__ << screenPoint << " returns " << result; + qCDebug(lcQpaScreen) <<__FUNCTION__ << screenPoint << " returns " << result; return result; } @@ -384,7 +523,7 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData) m_data.physicalSizeMM = newData.physicalSizeMM; if (m_data.hMonitor != newData.hMonitor) { - qCDebug(lcQpaWindows) << "Monitor" << m_data.name + qCDebug(lcQpaScreen) << "Monitor" << m_data.name << "has had its hMonitor handle changed from" << m_data.hMonitor << "to" << newData.hMonitor; m_data.hMonitor = newData.hMonitor; @@ -397,10 +536,14 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData) const bool dpiChanged = !qFuzzyCompare(m_data.dpi.first, newData.dpi.first) || !qFuzzyCompare(m_data.dpi.second, newData.dpi.second); const bool orientationChanged = m_data.orientation != newData.orientation; + const bool primaryChanged = (newData.flags & QWindowsScreenData::PrimaryScreen) + && !(m_data.flags & QWindowsScreenData::PrimaryScreen); m_data.dpi = newData.dpi; m_data.orientation = newData.orientation; m_data.geometry = newData.geometry; m_data.availableGeometry = newData.availableGeometry; + m_data.flags = (m_data.flags & ~QWindowsScreenData::PrimaryScreen) + | (newData.flags & QWindowsScreenData::PrimaryScreen); if (dpiChanged) { QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), @@ -413,6 +556,8 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData) QWindowSystemInterface::handleScreenGeometryChange(screen(), newData.geometry, newData.availableGeometry); } + if (primaryChanged) + QWindowSystemInterface::handlePrimaryScreenChanged(this); } HMONITOR QWindowsScreen::handle() const @@ -429,62 +574,50 @@ QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScre return result; } -enum OrientationPreference : DWORD // matching Win32 API ORIENTATION_PREFERENCE -{ - orientationPreferenceNone = 0, - orientationPreferenceLandscape = 0x1, - orientationPreferencePortrait = 0x2, - orientationPreferenceLandscapeFlipped = 0x4, - orientationPreferencePortraitFlipped = 0x8 -}; - bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o) { bool result = false; - if (QWindowsContext::user32dll.setDisplayAutoRotationPreferences) { - DWORD orientationPreference = 0; - switch (o) { - case Qt::PrimaryOrientation: - orientationPreference = orientationPreferenceNone; - break; - case Qt::PortraitOrientation: - orientationPreference = orientationPreferencePortrait; - break; - case Qt::LandscapeOrientation: - orientationPreference = orientationPreferenceLandscape; - break; - case Qt::InvertedPortraitOrientation: - orientationPreference = orientationPreferencePortraitFlipped; - break; - case Qt::InvertedLandscapeOrientation: - orientationPreference = orientationPreferenceLandscapeFlipped; - break; - } - result = QWindowsContext::user32dll.setDisplayAutoRotationPreferences(orientationPreference); + ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; + switch (o) { + case Qt::PrimaryOrientation: + break; + case Qt::PortraitOrientation: + orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT; + break; + case Qt::LandscapeOrientation: + orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE; + break; + case Qt::InvertedPortraitOrientation: + orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED; + break; + case Qt::InvertedLandscapeOrientation: + orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED; + break; } + result = SetDisplayAutoRotationPreferences(orientationPreference); return result; } Qt::ScreenOrientation QWindowsScreen::orientationPreference() { Qt::ScreenOrientation result = Qt::PrimaryOrientation; - if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences) { - DWORD orientationPreference = 0; - if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences(&orientationPreference)) { - switch (orientationPreference) { - case orientationPreferenceLandscape: - result = Qt::LandscapeOrientation; - break; - case orientationPreferencePortrait: - result = Qt::PortraitOrientation; - break; - case orientationPreferenceLandscapeFlipped: - result = Qt::InvertedLandscapeOrientation; - break; - case orientationPreferencePortraitFlipped: - result = Qt::InvertedPortraitOrientation; - break; - } + ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; + if (GetDisplayAutoRotationPreferences(&orientationPreference)) { + switch (orientationPreference) { + case ORIENTATION_PREFERENCE_NONE: + break; + case ORIENTATION_PREFERENCE_LANDSCAPE: + result = Qt::LandscapeOrientation; + break; + case ORIENTATION_PREFERENCE_PORTRAIT: + result = Qt::PortraitOrientation; + break; + case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED: + result = Qt::InvertedLandscapeOrientation; + break; + case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED: + result = Qt::InvertedPortraitOrientation; + break; } } return result; @@ -497,9 +630,9 @@ QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTy { QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint(); if (type == QPlatformScreen::Subpixel_None) { - QSettings settings(QLatin1String(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\DISPLAY1)"), + QSettings settings(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\DISPLAY1)"_L1, QSettings::NativeFormat); - int registryValue = settings.value(QLatin1String("PixelStructure"), -1).toInt(); + int registryValue = settings.value("PixelStructure"_L1, -1).toInt(); switch (registryValue) { case 0: type = QPlatformScreen::Subpixel_None; @@ -529,8 +662,46 @@ QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTy \internal */ +extern "C" LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_DISPLAYCHANGE) { + qCDebug(lcQpaScreen) << "Handling WM_DISPLAYCHANGE"; + if (QWindowsTheme *t = QWindowsTheme::instance()) + t->displayChanged(); + QWindowsWindow::displayChanged(); + if (auto *context = QWindowsContext::instance()) + context->screenManager().handleScreenChanges(); + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} + QWindowsScreenManager::QWindowsScreenManager() = default; +void QWindowsScreenManager::initialize() +{ + qCDebug(lcQpaScreen) << "Initializing screen manager"; + + auto className = QWindowsContext::instance()->registerWindowClass( + QWindowsContext::classNamePrefix() + QLatin1String("ScreenChangeObserverWindow"), + qDisplayChangeObserverWndProc); + + // HWND_MESSAGE windows do not get WM_DISPLAYCHANGE, so we need to create + // a real top level window that we never show. + m_displayChangeObserver = CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()), + nullptr, WS_TILED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + nullptr, nullptr, GetModuleHandle(nullptr), nullptr); + Q_ASSERT(m_displayChangeObserver); + + qCDebug(lcQpaScreen) << "Created display change observer" << m_displayChangeObserver; + + handleScreenChanges(); +} + +QWindowsScreenManager::~QWindowsScreenManager() +{ + DestroyWindow(m_displayChangeObserver); +} bool QWindowsScreenManager::isSingleScreen() { @@ -538,19 +709,19 @@ bool QWindowsScreenManager::isSingleScreen() } static inline int indexOfMonitor(const QWindowsScreenManager::WindowsScreenList &screens, - const QString &monitorName) + const QString &deviceName) { for (int i= 0; i < screens.size(); ++i) - if (screens.at(i)->data().name == monitorName) + if (screens.at(i)->data().deviceName == deviceName) return i; return -1; } static inline int indexOfMonitor(const WindowsScreenDataList &screenData, - const QString &monitorName) + const QString &deviceName) { for (int i = 0; i < screenData.size(); ++i) - if (screenData.at(i).name == monitorName) + if (screenData.at(i).deviceName == deviceName) return i; return -1; } @@ -574,7 +745,7 @@ static void moveToVirtualScreen(QWindow *w, const QScreen *newScreen) void QWindowsScreenManager::removeScreen(int index) { - qCDebug(lcQpaWindows) << "Removing Monitor:" << m_screens.at(index)->data(); + qCDebug(lcQpaScreen) << "Removing Monitor:" << m_screens.at(index)->data(); QScreen *screen = m_screens.at(index)->screen(); QScreen *primaryScreen = QGuiApplication::primaryScreen(); // QTBUG-38650: When a screen is disconnected, Windows will automatically @@ -616,7 +787,7 @@ bool QWindowsScreenManager::handleScreenChanges() const bool lockScreen = newDataList.size() == 1 && (newDataList.front().flags & QWindowsScreenData::LockScreen); bool primaryScreenChanged = false; for (const QWindowsScreenData &newData : newDataList) { - const int existingIndex = indexOfMonitor(m_screens, newData.name); + const int existingIndex = indexOfMonitor(m_screens, newData.deviceName); if (existingIndex != -1) { m_screens.at(existingIndex)->handleChanges(newData); if (existingIndex == 0) @@ -626,14 +797,14 @@ bool QWindowsScreenManager::handleScreenChanges() m_screens.push_back(newScreen); QWindowSystemInterface::handleScreenAdded(newScreen, newData.flags & QWindowsScreenData::PrimaryScreen); - qCDebug(lcQpaWindows) << "New Monitor: " << newData; + qCDebug(lcQpaScreen) << "New Monitor: " << newData; } // exists } // for new screens. // Remove deleted ones but keep main monitors if we get only the // temporary lock screen to avoid window recreation (QTBUG-33062). if (!lockScreen) { for (int i = m_screens.size() - 1; i >= 0; --i) { - if (indexOfMonitor(newDataList, m_screens.at(i)->data().name) == -1) + if (indexOfMonitor(newDataList, m_screens.at(i)->data().deviceName) == -1) removeScreen(i); } // for existing screens } // not lock screen diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 2052648933..0467ab2a0c 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSSCREEN_H #define QWINDOWSSCREEN_H @@ -43,9 +7,9 @@ #include "qtwindowsglobal.h" #include <QtCore/qlist.h> -#include <QtCore/qpair.h> #include <QtCore/qscopedpointer.h> #include <qpa/qplatformscreen.h> +#include <QtGui/qscreen_platform.h> QT_BEGIN_NAMESPACE @@ -66,12 +30,18 @@ struct QWindowsScreenData QImage::Format format = QImage::Format_ARGB32_Premultiplied; unsigned flags = VirtualDesktop; QString name; + QString manufacturer; + QString model; + QString serialNumber; Qt::ScreenOrientation orientation = Qt::LandscapeOrientation; qreal refreshRateHz = 60; HMONITOR hMonitor = nullptr; + QString deviceName; + QString devicePath; + std::optional<int> deviceIndex = std::nullopt; }; -class QWindowsScreen : public QPlatformScreen +class QWindowsScreen : public QPlatformScreen, public QNativeInterface::QWindowsScreen { public: #ifndef QT_NO_CURSOR @@ -86,10 +56,13 @@ public: QImage::Format format() const override { return m_data.format; } QSizeF physicalSize() const override { return m_data.physicalSizeMM; } QDpi logicalDpi() const override { return m_data.dpi; } - QDpi logicalBaseDpi() const override { return QDpi(96, 96); } + QDpi logicalBaseDpi() const override { return QDpi(baseDpi, baseDpi); } qreal devicePixelRatio() const override { return 1.0; } qreal refreshRate() const override { return m_data.refreshRateHz; } - QString name() const override { return m_data.name; } + QString name() const override; + QString manufacturer() const override { return m_data.manufacturer; } + QString model() const override { return m_data.model; } + QString serialNumber() const override { return m_data.serialNumber; } Qt::ScreenOrientation orientation() const override { return m_data.orientation; } QList<QPlatformScreen *> virtualSiblings() const override; QWindow *topLevelAt(const QPoint &point) const override; @@ -103,7 +76,7 @@ public: inline void handleChanges(const QWindowsScreenData &newData); - HMONITOR handle() const; + HMONITOR handle() const override; #ifndef QT_NO_CURSOR QPlatformCursor *cursor() const override { return m_cursor.data(); } @@ -115,6 +88,7 @@ public: const QWindowsScreenData &data() const { return m_data; } static QRect virtualGeometry(const QPlatformScreen *screen); + static inline int baseDpi = 96; private: QWindowsScreenData m_data; @@ -125,10 +99,13 @@ private: class QWindowsScreenManager { + Q_DISABLE_COPY_MOVE(QWindowsScreenManager) public: using WindowsScreenList = QList<QWindowsScreen *>; QWindowsScreenManager(); + void initialize(); + ~QWindowsScreenManager(); void clearScreens(); @@ -143,6 +120,7 @@ public: private: void removeScreen(int index); + HWND m_displayChangeObserver = nullptr; WindowsScreenList m_screens; }; diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index fe2d8a36c0..89f93fd161 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -1,43 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#define QT_NO_URL_CAST_FROM_STRING +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + #include "qwindowsservices.h" #include <QtCore/qt_windows.h> @@ -48,34 +11,88 @@ #include <QtCore/qthread.h> #include <QtCore/private/qwinregistry_p.h> +#include <QtCore/private/qfunctions_win_p.h> #include <shlobj.h> +#include <shlwapi.h> #include <intshcut.h> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + 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); - CoUninitialize(); - } + QComHelper comHelper; + if (comHelper.isValid()) + m_result = ShellExecute(nullptr, m_operation, m_file, m_parameters, nullptr, + SW_SHOWNORMAL); } HINSTANCE result() const { return m_result; } 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() @@ -85,7 +102,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(); @@ -93,7 +112,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; @@ -111,15 +130,15 @@ static inline QString mailCommand() // Check if user has set preference, otherwise use default. QString keyName = QWinRegistryKey(HKEY_CURRENT_USER, mailUserKey) .stringValue( L"Progid"); - const QLatin1String mailto = keyName.isEmpty() ? QLatin1String("mailto") : QLatin1String(); - keyName += mailto + QLatin1String("\\Shell\\Open\\Command"); + const auto mailto = keyName.isEmpty() ? "mailto"_L1 : QLatin1StringView(); + keyName += mailto + "\\Shell\\Open\\Command"_L1; if (debug) qDebug() << __FUNCTION__ << "keyName=" << keyName; const QString command = QWinRegistryKey(HKEY_CLASSES_ROOT, keyName).stringValue(L""); // QTBUG-57816: As of Windows 10, if there is no mail client installed, an entry like // "rundll32.exe .. url.dll,MailToProtocolHandler %l" is returned. Launching it // silently fails or brings up a broken dialog after a long time, so exclude it and - // fall back to ShellExecute() which brings up the URL assocation dialog. + // fall back to ShellExecute() which brings up the URL association dialog. if (command.isEmpty() || command.contains(u",MailToProtocolHandler")) return QString(); wchar_t expandedCommand[MAX_PATH] = {0}; @@ -135,10 +154,15 @@ static inline bool launchMail(const QUrl &url) qWarning("Cannot launch '%ls': There is no mail program installed.", qUtf16Printable(url.toString())); return false; } + // Fix mail launch if no param is expected in this command. + if (command.indexOf("%1"_L1) < 0) { + qWarning() << "The mail command lacks the '%1' parameter."; + return false; + } //Make sure the path for the process is in quotes const QChar doubleQuote = u'"'; if (!command.startsWith(doubleQuote)) { - const int exeIndex = command.indexOf(QStringLiteral(".exe "), 0, Qt::CaseInsensitive); + const int exeIndex = command.indexOf(".exe "_L1, 0, Qt::CaseInsensitive); if (exeIndex != -1) { command.insert(exeIndex + 4, doubleQuote); command.prepend(doubleQuote); @@ -146,7 +170,7 @@ static inline bool launchMail(const QUrl &url) } // Pass the url as the parameter. Should use QProcess::startDetached(), // but that cannot handle a Windows command line [yet]. - command.replace(QLatin1String("%1"), url.toString(QUrl::FullyEncoded)); + command.replace("%1"_L1, url.toString(QUrl::FullyEncoded)); if (debug) qDebug() << __FUNCTION__ << "Launching" << command; //start the process @@ -170,7 +194,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/qwindowsservices.h b/src/plugins/platforms/windows/qwindowsservices.h index 5feb7c8490..56279f57fc 100644 --- a/src/plugins/platforms/windows/qwindowsservices.h +++ b/src/plugins/platforms/windows/qwindowsservices.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSDESKTOPSERVICES_H #define QWINDOWSDESKTOPSERVICES_H diff --git a/src/plugins/platforms/windows/qwindowssessionmanager.cpp b/src/plugins/platforms/windows/qwindowssessionmanager.cpp index 500fdc750c..487ebeba65 100644 --- a/src/plugins/platforms/windows/qwindowssessionmanager.cpp +++ b/src/plugins/platforms/windows/qwindowssessionmanager.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowssessionmanager.h" diff --git a/src/plugins/platforms/windows/qwindowssessionmanager.h b/src/plugins/platforms/windows/qwindowssessionmanager.h index 0769ed1fce..320c1aedee 100644 --- a/src/plugins/platforms/windows/qwindowssessionmanager.h +++ b/src/plugins/platforms/windows/qwindowssessionmanager.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSSESSIONMANAGER_H #define QWINDOWSSESSIONMANAGER_H diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp index 7c473e66b6..6f0680ac23 100644 --- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp +++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp @@ -1,55 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#if defined(WINVER) && WINVER < 0x0601 -# undef WINVER -#endif -#if !defined(WINVER) -# define WINVER 0x0601 // required for NOTIFYICONDATA_V2_SIZE, ChangeWindowMessageFilterEx() (MinGW 5.3) -#endif - -#if defined(NTDDI_VERSION) && NTDDI_VERSION < 0x06010000 -# undef NTDDI_VERSION -#endif -#if !defined(NTDDI_VERSION) -# define NTDDI_VERSION 0x06010000 // required for Shell_NotifyIconGetRect (MinGW 5.3) -#endif +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtCore/qt_windows.h> #include "qwindowssystemtrayicon.h" #include "qwindowscontext.h" @@ -65,7 +17,6 @@ #include <QtCore/qsettings.h> #include <qpa/qwindowsysteminterface.h> -#include <qt_windows.h> #include <commctrl.h> #include <shellapi.h> #include <shlobj.h> @@ -73,6 +24,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::Literals::StringLiterals; + static const UINT q_uNOTIFYICONID = 0; static uint MYWM_TASKBARCREATED = 0; @@ -136,9 +89,6 @@ static int indexOfHwnd(HWND hwnd) extern "C" LRESULT QT_WIN_CALLBACK qWindowsTrayIconWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - // QTBUG-79248: Trigger screen update if there are no other windows. - if (message == WM_DPICHANGED && QGuiApplication::topLevelWindows().isEmpty()) - QWindowsContext::instance()->screenManager().handleScreenChanges(); if (message == MYWM_TASKBARCREATED || message == MYWM_NOTIFYICON || message == WM_INITMENU || message == WM_INITMENUPOPUP || message == WM_CLOSE || message == WM_COMMAND) { @@ -168,7 +118,7 @@ static inline HWND createTrayIconMessageWindow() return nullptr; // Register window class in the platform plugin. const QString className = - ctx->registerWindowClass(QWindowsContext::classNamePrefix() + QStringLiteral("TrayIconMessageWindowClass"), + ctx->registerWindowClass(QWindowsContext::classNamePrefix() + "TrayIconMessageWindowClass"_L1, qWindowsTrayIconWndProc); const wchar_t windowName[] = L"QTrayIconMessageWindow"; return CreateWindowEx(0, reinterpret_cast<const wchar_t *>(className.utf16()), @@ -214,8 +164,7 @@ void QWindowsSystemTrayIcon::cleanup() 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); @@ -235,6 +184,9 @@ void QWindowsSystemTrayIcon::updateToolTip(const QString &tooltip) QRect QWindowsSystemTrayIcon::geometry() const { + if (!isIconVisible()) + return QRect(); + NOTIFYICONIDENTIFIER nid; memset(&nid, 0, sizeof(nid)); nid.cbSize = sizeof(nid); @@ -268,25 +220,19 @@ void QWindowsSystemTrayIcon::showMessage(const QString &title, const QString &me qStringToLimitedWCharArray(title, tnd.szInfoTitle, 64); tnd.uID = q_uNOTIFYICONID; - tnd.dwInfoFlags = NIIF_USER; - - QSize size(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); - const QSize largeIcon(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); - const QSize more = icon.actualSize(largeIcon); - if (more.height() > (largeIcon.height() * 3/4) || more.width() > (largeIcon.width() * 3/4)) { - tnd.dwInfoFlags |= NIIF_LARGE_ICON; - size = largeIcon; - } + + const auto size = icon.actualSize(QSize(256, 256)); QPixmap pm = icon.pixmap(size); + if (m_hMessageIcon) { + DestroyIcon(m_hMessageIcon); + m_hMessageIcon = nullptr; + } if (pm.isNull()) { tnd.dwInfoFlags = NIIF_INFO; } else { - if (pm.size() != size) { - qWarning("QSystemTrayIcon::showMessage: Wrong icon size (%dx%d), please add standard one: %dx%d", - pm.size().width(), pm.size().height(), size.width(), size.height()); - pm = pm.scaled(size, Qt::IgnoreAspectRatio); - } - tnd.hBalloonIcon = qt_pixmapToWinHICON(pm); + tnd.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; + m_hMessageIcon = qt_pixmapToWinHICON(pm); + tnd.hBalloonIcon = m_hMessageIcon; } tnd.hWnd = m_hwnd; tnd.uTimeout = msecsIn <= 0 ? UINT(10000) : UINT(msecsIn); // 10s default @@ -344,7 +290,10 @@ void QWindowsSystemTrayIcon::ensureCleanup() } if (m_hIcon != nullptr) DestroyIcon(m_hIcon); + if (m_hMessageIcon != nullptr) + DestroyIcon(m_hMessageIcon); m_hIcon = nullptr; + m_hMessageIcon = nullptr; m_menu = nullptr; // externally owned m_toolTip.clear(); } @@ -361,6 +310,29 @@ bool QWindowsSystemTrayIcon::setIconVisible(bool visible) return Shell_NotifyIcon(NIM_MODIFY, &tnd) == TRUE; } +bool QWindowsSystemTrayIcon::isIconVisible() const +{ + NOTIFYICONIDENTIFIER nid; + memset(&nid, 0, sizeof(nid)); + nid.cbSize = sizeof(nid); + nid.hWnd = m_hwnd; + nid.uID = q_uNOTIFYICONID; + RECT rect; + const HRESULT hr = Shell_NotifyIconGetRect(&nid, &rect); + // Windows 10 returns S_FALSE if the icon is hidden + if (FAILED(hr) || hr == S_FALSE) + return false; + + HMONITOR monitor = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(monitor, &info); + // Windows 11 seems to return a geometry outside of the current monitor's geometry in case of + // the icon being hidden. As it's impossible to change the alignment of the task bar on Windows + // 11 this check should be fine. + return rect.bottom <= info.rcMonitor.bottom; +} + bool QWindowsSystemTrayIcon::sendTrayMessage(DWORD msg) { NOTIFYICONDATA tnd; @@ -454,8 +426,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/qwindowssystemtrayicon.h b/src/plugins/platforms/windows/qwindowssystemtrayicon.h index 44e1bcc761..3ad5feb125 100644 --- a/src/plugins/platforms/windows/qwindowssystemtrayicon.h +++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSSYSTEMTRAYICON_H #define QWINDOWSSYSTEMTRAYICON_H @@ -85,12 +49,14 @@ private: void ensureCleanup(); bool sendTrayMessage(DWORD msg); bool setIconVisible(bool visible); + bool isIconVisible() const; HICON createIcon(const QIcon &icon); QIcon m_icon; QString m_toolTip; HWND m_hwnd = nullptr; HICON m_hIcon = nullptr; + HICON m_hMessageIcon = nullptr; mutable QPointer<QWindowsPopupMenu> m_menu; bool m_ignoreNextMouseRelease = false; bool m_visible = false; diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp index fe4a0d4054..ceebb483d2 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowstabletsupport.h" @@ -631,7 +595,8 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() << "mode=" << m_mode; } - const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + const Qt::KeyboardModifiers keyboardModifiers = keyMapper->queryKeyboardModifiers(); for (int i = 0; i < packetCount ; ++i) { const PACKET &packet = localPacketBuf[i]; diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.h b/src/plugins/platforms/windows/qwindowstabletsupport.h index dca2800215..fb639294d3 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.h +++ b/src/plugins/platforms/windows/qwindowstabletsupport.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSTABLETSUPPORT_H #define QWINDOWSTABLETSUPPORT_H diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 6fbf4183da..e8a324aedb 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -1,75 +1,35 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// SHSTOCKICONINFO is only available since Vista -#if _WIN32_WINNT < 0x0601 -# undef _WIN32_WINNT -# define _WIN32_WINNT 0x0601 -#endif +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtCore/qt_windows.h> #include "qwindowstheme.h" #include "qwindowsmenu.h" #include "qwindowsdialoghelpers.h" #include "qwindowscontext.h" +#include "qwindowsiconengine.h" #include "qwindowsintegration.h" #if QT_CONFIG(systemtrayicon) # include "qwindowssystemtrayicon.h" #endif #include "qwindowsscreen.h" -#include "qt_windows.h" +#include "qwindowswindow.h" #include <commctrl.h> #include <objbase.h> -#ifndef Q_CC_MINGW -# include <commoncontrols.h> -#endif +#include <commoncontrols.h> #include <shellapi.h> +#include <QtCore/qapplicationstatic.h> #include <QtCore/qvariant.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qdebug.h> -#include <QtCore/qtextstream.h> -#include <QtCore/qoperatingsystemversion.h> #include <QtCore/qsysinfo.h> #include <QtCore/qcache.h> #include <QtCore/qthread.h> +#include <QtCore/qqueue.h> #include <QtCore/qmutex.h> #include <QtCore/qwaitcondition.h> +#include <QtCore/qoperatingsystemversion.h> #include <QtGui/qcolor.h> #include <QtGui/qpalette.h> #include <QtGui/qguiapplication.h> @@ -81,30 +41,19 @@ #include <private/qhighdpiscaling_p.h> #include <private/qsystemlibrary_p.h> #include <private/qwinregistry_p.h> +#include <QtCore/private/qfunctions_win_p.h> #include <algorithm> -#if defined(__IImageList_INTERFACE_DEFINED__) && defined(__IID_DEFINED__) -# define USE_IIMAGELIST -#endif +#if QT_CONFIG(cpp_winrt) +# include <QtCore/private/qt_winrtbase_p.h> -QT_BEGIN_NAMESPACE +# include <winrt/Windows.UI.ViewManagement.h> +#endif // QT_CONFIG(cpp_winrt) -static inline QColor COLORREFToQColor(COLORREF cr) -{ - return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); -} +QT_BEGIN_NAMESPACE -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; -} +using namespace Qt::StringLiterals; static inline bool booleanSystemParametersInfo(UINT what, bool defaultValue) { @@ -129,128 +78,159 @@ static inline QColor mixColors(const QColor &c1, const QColor &c2) (c1.blue() + c2.blue()) / 2}; } +enum AccentColorLevel { + AccentColorDarkest, + AccentColorNormal, + AccentColorLightest +}; + +#if QT_CONFIG(cpp_winrt) +static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color) +{ + return QColor(color.R, color.G, color.B, color.A); +} +#endif + +[[maybe_unused]] [[nodiscard]] static inline QColor qt_accentColor(AccentColorLevel level) +{ +#if QT_CONFIG(cpp_winrt) + using namespace winrt::Windows::UI::ViewManagement; + const auto settings = UISettings(); + const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); + const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); + const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); +#else + const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); + if (!registry.isValid()) + return {}; + const QVariant value = registry.value(L"AccentColor"); + if (!value.isValid()) + return {}; + // The retrieved value is in the #AABBGGRR format, we need to + // convert it to the #AARRGGBB format which Qt expects. + const QColor abgr = QColor::fromRgba(qvariant_cast<DWORD>(value)); + if (!abgr.isValid()) + return {}; + const QColor accent = QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha()); + const QColor accentLight = accent.lighter(120); + const QColor accentDarkest = accent.darker(120 * 120 * 120); +#endif + if (level == AccentColorDarkest) + return accentDarkest; + else if (level == AccentColorLightest) + return accentLight; + return accent; +} + static inline QColor getSysColor(int index) { - return COLORREFToQColor(GetSysColor(index)); + COLORREF cr = GetSysColor(index); + return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); } // QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system // models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the // behavior by running it in a thread. - -struct QShGetFileInfoParams -{ - QShGetFileInfoParams(const QString &fn, DWORD a, SHFILEINFO *i, UINT f, bool *r) - : fileName(fn), attributes(a), flags(f), info(i), result(r) - { } - - const QString &fileName; - const DWORD attributes; - const UINT flags; - SHFILEINFO *const info; - bool *const result; -}; - class QShGetFileInfoThread : public QThread { public: - explicit QShGetFileInfoThread() - : QThread(), m_params(nullptr) + struct Task + { + Task(const QString &fn, DWORD a, UINT f) + : fileName(fn), attributes(a), flags(f) + {} + Q_DISABLE_COPY(Task) + ~Task() + { + DestroyIcon(hIcon); + hIcon = 0; + } + // Request + const QString fileName; + const DWORD attributes; + const UINT flags; + // Result + HICON hIcon = 0; + int iIcon = -1; + bool finished = false; + bool resultValid() const { return hIcon != 0 && iIcon >= 0 && finished; } + }; + + QShGetFileInfoThread() + : QThread() { - connect(this, &QThread::finished, this, &QObject::deleteLater); + start(); } - void run() override + ~QShGetFileInfoThread() { - m_init = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + cancel(); + wait(); + } + + QSharedPointer<Task> getNextTask() + { + QMutexLocker l(&m_waitForTaskMutex); + while (!isInterruptionRequested()) { + if (!m_taskQueue.isEmpty()) + return m_taskQueue.dequeue(); + m_waitForTaskCondition.wait(&m_waitForTaskMutex); + } + return nullptr; + } - QMutexLocker readyLocker(&m_readyMutex); - while (!m_cancelled.loadRelaxed()) { - if (!m_params && !m_cancelled.loadRelaxed() - && !m_readyCondition.wait(&m_readyMutex, QDeadlineTimer(1000ll))) - continue; + void run() override + { + QComHelper comHelper(COINIT_MULTITHREADED); - if (m_params) { - const QString fileName = m_params->fileName; + while (!isInterruptionRequested()) { + auto task = getNextTask(); + if (task) { SHFILEINFO info; - const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(fileName.utf16()), - m_params->attributes, &info, sizeof(SHFILEINFO), - m_params->flags); - m_doneMutex.lock(); - if (!m_cancelled.loadRelaxed()) { - *m_params->result = result; - memcpy(m_params->info, &info, sizeof(SHFILEINFO)); + const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(task->fileName.utf16()), + task->attributes, &info, sizeof(SHFILEINFO), + task->flags); + if (result) { + task->hIcon = info.hIcon; + task->iIcon = info.iIcon; } - m_params = nullptr; - + task->finished = true; m_doneCondition.wakeAll(); - m_doneMutex.unlock(); } } - - if (m_init != S_FALSE) - CoUninitialize(); } - bool runWithParams(QShGetFileInfoParams *params, qint64 timeOutMSecs) + void runWithParams(const QSharedPointer<Task> &task, + std::chrono::milliseconds timeout = std::chrono::milliseconds(5000)) { - QMutexLocker doneLocker(&m_doneMutex); - - m_readyMutex.lock(); - m_params = params; - m_readyCondition.wakeAll(); - m_readyMutex.unlock(); + { + QMutexLocker l(&m_waitForTaskMutex); + m_taskQueue.enqueue(task); + m_waitForTaskCondition.wakeAll(); + } - return m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeOutMSecs)); + QMutexLocker doneLocker(&m_doneMutex); + while (!task->finished && !isInterruptionRequested()) { + if (!m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeout))) + return; + } } void cancel() { - QMutexLocker doneLocker(&m_doneMutex); - m_cancelled.storeRelaxed(1); - m_readyCondition.wakeAll(); + requestInterruption(); + m_doneCondition.wakeAll(); + m_waitForTaskCondition.wakeAll(); } private: - HRESULT m_init; - QShGetFileInfoParams *m_params; - QAtomicInt m_cancelled; - QWaitCondition m_readyCondition; + QQueue<QSharedPointer<Task>> m_taskQueue; QWaitCondition m_doneCondition; - QMutex m_readyMutex; + QWaitCondition m_waitForTaskCondition; QMutex m_doneMutex; + QMutex m_waitForTaskMutex; }; - -static bool shGetFileInfoBackground(const QString &fileName, DWORD attributes, - SHFILEINFO *info, UINT flags, - qint64 timeOutMSecs = 5000) -{ - static QShGetFileInfoThread *getFileInfoThread = nullptr; - if (!getFileInfoThread) { - getFileInfoThread = new QShGetFileInfoThread; - getFileInfoThread->start(); - } - - bool result = false; - QShGetFileInfoParams params(fileName, attributes, info, flags, &result); - if (!getFileInfoThread->runWithParams(¶ms, timeOutMSecs)) { - // Cancel and reset getFileInfoThread. It'll - // be reinitialized the next time we get called. - getFileInfoThread->cancel(); - getFileInfoThread = nullptr; - qWarning().noquote() << "SHGetFileInfo() timed out for " << fileName; - return false; - } - return result; -} - -// Dark Mode constants -enum DarkModeColors : QRgb { - darkModeBtnHighlightRgb = 0xc0c0c0, - darkModeBtnShadowRgb = 0x808080, - darkModeHighlightRgb = 0x0055ff, // deviating from 0x800080 - darkModeMenuHighlightRgb = darkModeHighlightRgb -}; +Q_APPLICATION_STATIC(QShGetFileInfoThread, s_shGetFileInfoThread) // from QStyle::standardPalette static inline QPalette standardPalette() @@ -275,16 +255,28 @@ static QColor placeHolderColor(QColor textColor) return textColor; } -static void populateLightSystemBasePalette(QPalette &result) +/* + This is used when the theme is light mode, and when the theme is dark but the + application doesn't support dark mode. In the latter case, we need to check. +*/ +void QWindowsTheme::populateLightSystemBasePalette(QPalette &result) { + const QColor background = getSysColor(COLOR_BTNFACE); + const QColor textColor = getSysColor(COLOR_WINDOWTEXT); + + const QColor accent = qt_accentColor(AccentColorNormal); + const QColor accentDarkest = qt_accentColor(AccentColorDarkest); + + const QColor linkColor = accent; + const QColor btnFace = background; + const QColor btnHighlight = getSysColor(COLOR_BTNHIGHLIGHT); + + result.setColor(QPalette::Highlight, getSysColor(COLOR_HIGHLIGHT)); result.setColor(QPalette::WindowText, getSysColor(COLOR_WINDOWTEXT)); - const QColor btnFace = getSysColor(COLOR_BTNFACE); result.setColor(QPalette::Button, btnFace); - const QColor btnHighlight = getSysColor(COLOR_BTNHIGHLIGHT); result.setColor(QPalette::Light, btnHighlight); result.setColor(QPalette::Dark, getSysColor(COLOR_BTNSHADOW)); result.setColor(QPalette::Mid, result.button().color().darker(150)); - const QColor textColor = getSysColor(COLOR_WINDOWTEXT); result.setColor(QPalette::Text, textColor); result.setColor(QPalette::PlaceholderText, placeHolderColor(textColor)); result.setColor(QPalette::BrightText, btnHighlight); @@ -293,42 +285,11 @@ static void populateLightSystemBasePalette(QPalette &result) result.setColor(QPalette::ButtonText, getSysColor(COLOR_BTNTEXT)); result.setColor(QPalette::Midlight, getSysColor(COLOR_3DLIGHT)); result.setColor(QPalette::Shadow, getSysColor(COLOR_3DDKSHADOW)); - result.setColor(QPalette::Highlight, getSysColor(COLOR_HIGHLIGHT)); result.setColor(QPalette::HighlightedText, getSysColor(COLOR_HIGHLIGHTTEXT)); -} + result.setColor(QPalette::Accent, accent); -static void populateDarkSystemBasePalette(QPalette &result) -{ - const QColor darkModeWindowText = Qt::white; - result.setColor(QPalette::WindowText, darkModeWindowText); - const QColor darkModebtnFace = Qt::black; - result.setColor(QPalette::Button, darkModebtnFace); - const QColor btnHighlight = QColor(darkModeBtnHighlightRgb); - result.setColor(QPalette::Light, btnHighlight); - result.setColor(QPalette::Dark, QColor(darkModeBtnShadowRgb)); - result.setColor(QPalette::Mid, result.button().color().darker(150)); - result.setColor(QPalette::Text, darkModeWindowText); - result.setColor(QPalette::PlaceholderText, placeHolderColor(darkModeWindowText)); - result.setColor(QPalette::BrightText, btnHighlight); - result.setColor(QPalette::Base, darkModebtnFace); - result.setColor(QPalette::Window, darkModebtnFace); - result.setColor(QPalette::ButtonText, darkModeWindowText); - result.setColor(QPalette::Midlight, darkModeWindowText); - result.setColor(QPalette::Shadow, darkModeWindowText); - result.setColor(QPalette::Highlight, QColor(darkModeHighlightRgb)); - result.setColor(QPalette::HighlightedText, darkModeWindowText); -} - -static QPalette systemPalette(bool light) -{ - QPalette result = standardPalette(); - if (light) - populateLightSystemBasePalette(result); - else - populateDarkSystemBasePalette(result); - - result.setColor(QPalette::Link, Qt::blue); - result.setColor(QPalette::LinkVisited, Qt::magenta); + result.setColor(QPalette::Link, linkColor); + result.setColor(QPalette::LinkVisited, accentDarkest); result.setColor(QPalette::Inactive, QPalette::Button, result.button().color()); result.setColor(QPalette::Inactive, QPalette::Window, result.window().color()); result.setColor(QPalette::Inactive, QPalette::Light, result.light().color()); @@ -336,35 +297,79 @@ static QPalette systemPalette(bool light) if (result.midlight() == result.button()) result.setColor(QPalette::Midlight, result.button().color().lighter(110)); - if (result.window() != result.base()) { - result.setColor(QPalette::Inactive, QPalette::Highlight, result.color(QPalette::Inactive, QPalette::Window)); - result.setColor(QPalette::Inactive, QPalette::HighlightedText, result.color(QPalette::Inactive, QPalette::Text)); - } - - const QColor disabled = - mixColors(result.windowText().color(), result.button().color()); +} - result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(), - result.light(), result.dark(), result.mid(), - result.text(), result.brightText(), result.base(), - result.window()); - result.setColor(QPalette::Disabled, QPalette::WindowText, disabled); - result.setColor(QPalette::Disabled, QPalette::Text, disabled); - result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled); - result.setColor(QPalette::Disabled, QPalette::Highlight, - light ? getSysColor(COLOR_HIGHLIGHT) : QColor(darkModeHighlightRgb)); - result.setColor(QPalette::Disabled, QPalette::HighlightedText, - light ? getSysColor(COLOR_HIGHLIGHTTEXT) : QColor(Qt::white)); - result.setColor(QPalette::Disabled, QPalette::Base, - result.window().color()); - return result; +void QWindowsTheme::populateDarkSystemBasePalette(QPalette &result) +{ +#if QT_CONFIG(cpp_winrt) + using namespace winrt::Windows::UI::ViewManagement; + const auto settings = UISettings(); + + // We have to craft a palette from these colors. The settings.UIElementColor(UIElementType) API + // returns the old system colors, not the dark mode colors. If the background is black (which it + // usually), then override it with a dark gray instead so that we can go up and down the lightness. + const QColor foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground)); + const QColor background = [&settings]() -> QColor { + auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background)); + if (systemBackground == Qt::black) + systemBackground = QColor(0x1E, 0x1E, 0x1E); + return systemBackground; + }(); + + const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); + const QColor accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1)); + const QColor accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2)); + const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); + const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); + const QColor accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2)); + const QColor accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3)); +#else + const QColor foreground = Qt::white; + const QColor background = QColor(0x1E, 0x1E, 0x1E); + const QColor accent = qt_accentColor(AccentColorNormal); + const QColor accentDark = accent.darker(120); + const QColor accentDarker = accentDark.darker(120); + const QColor accentDarkest = accentDarker.darker(120); + const QColor accentLight = accent.lighter(120); + const QColor accentLighter = accentLight.lighter(120); + const QColor accentLightest = accentLighter.lighter(120); +#endif + const QColor linkColor = accent; + const QColor buttonColor = background.lighter(200); + + result.setColor(QPalette::All, QPalette::WindowText, foreground); + result.setColor(QPalette::All, QPalette::Text, foreground); + result.setColor(QPalette::All, QPalette::BrightText, accentLightest); + + result.setColor(QPalette::All, QPalette::Button, buttonColor); + result.setColor(QPalette::All, QPalette::ButtonText, foreground); + result.setColor(QPalette::All, QPalette::Light, buttonColor.lighter(200)); + result.setColor(QPalette::All, QPalette::Midlight, buttonColor.lighter(150)); + result.setColor(QPalette::All, QPalette::Dark, buttonColor.darker(200)); + result.setColor(QPalette::All, QPalette::Mid, buttonColor.darker(150)); + result.setColor(QPalette::All, QPalette::Shadow, Qt::black); + + result.setColor(QPalette::All, QPalette::Base, background.lighter(150)); + result.setColor(QPalette::All, QPalette::Window, background); + + result.setColor(QPalette::All, QPalette::Highlight, accent); + result.setColor(QPalette::All, QPalette::HighlightedText, accent.lightness() > 128 ? Qt::black : Qt::white); + result.setColor(QPalette::All, QPalette::Link, linkColor); + result.setColor(QPalette::All, QPalette::LinkVisited, accentDarkest); + result.setColor(QPalette::All, QPalette::AlternateBase, accentDarkest); + result.setColor(QPalette::All, QPalette::ToolTipBase, buttonColor); + result.setColor(QPalette::All, QPalette::ToolTipText, foreground.darker(120)); + result.setColor(QPalette::All, QPalette::PlaceholderText, placeHolderColor(foreground)); + result.setColor(QPalette::All, QPalette::Accent, accent); } static inline QPalette toolTipPalette(const QPalette &systemPalette, bool light) { QPalette result(systemPalette); - const QColor tipBgColor = light ? getSysColor(COLOR_INFOBK) : QColor(Qt::black); - const QColor tipTextColor = light ? getSysColor(COLOR_INFOTEXT) : QColor(Qt::white); + const QColor tipBgColor = light ? getSysColor(COLOR_INFOBK) + : systemPalette.button().color(); + const QColor tipTextColor = light ? getSysColor(COLOR_INFOTEXT) + : systemPalette.buttonText().color().darker(120); result.setColor(QPalette::All, QPalette::Button, tipBgColor); result.setColor(QPalette::All, QPalette::Window, tipBgColor); @@ -378,8 +383,7 @@ static inline QPalette toolTipPalette(const QPalette &systemPalette, bool light) result.setColor(QPalette::All, QPalette::ButtonText, tipTextColor); result.setColor(QPalette::All, QPalette::ToolTipBase, tipBgColor); result.setColor(QPalette::All, QPalette::ToolTipText, tipTextColor); - const QColor disabled = - mixColors(result.windowText().color(), result.button().color()); + const QColor disabled = mixColors(result.windowText().color(), result.button().color()); result.setColor(QPalette::Disabled, QPalette::WindowText, disabled); result.setColor(QPalette::Disabled, QPalette::Text, disabled); result.setColor(QPalette::Disabled, QPalette::ToolTipText, disabled); @@ -391,11 +395,13 @@ static inline QPalette toolTipPalette(const QPalette &systemPalette, bool light) static inline QPalette menuPalette(const QPalette &systemPalette, bool light) { + if (!light) + return systemPalette; + QPalette result(systemPalette); - const QColor menuColor = light ? getSysColor(COLOR_MENU) : QColor(Qt::black); - const QColor menuTextColor = light ? getSysColor(COLOR_MENUTEXT) : QColor(Qt::white); - const QColor disabled = light - ? getSysColor(COLOR_GRAYTEXT) : QColor(darkModeBtnHighlightRgb); + const QColor menuColor = getSysColor(COLOR_MENU); + const QColor menuTextColor = getSysColor(COLOR_MENUTEXT); + const QColor disabled = getSysColor(COLOR_GRAYTEXT); // we might need a special color group for the result. result.setColor(QPalette::Active, QPalette::Button, menuColor); result.setColor(QPalette::Active, QPalette::Text, menuTextColor); @@ -404,9 +410,7 @@ static inline QPalette menuPalette(const QPalette &systemPalette, bool light) result.setColor(QPalette::Disabled, QPalette::WindowText, disabled); result.setColor(QPalette::Disabled, QPalette::Text, disabled); const bool isFlat = booleanSystemParametersInfo(SPI_GETFLATMENU, false); - const QColor highlightColor = light - ? (getSysColor(isFlat ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT)) - : QColor(darkModeMenuHighlightRgb); + const QColor highlightColor = getSysColor(isFlat ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT); result.setColor(QPalette::Disabled, QPalette::Highlight, highlightColor); result.setColor(QPalette::Disabled, QPalette::HighlightedText, disabled); result.setColor(QPalette::Disabled, QPalette::Button, @@ -431,13 +435,14 @@ static inline QPalette menuPalette(const QPalette &systemPalette, bool light) static inline QPalette *menuBarPalette(const QPalette &menuPalette, bool light) { QPalette *result = nullptr; - if (booleanSystemParametersInfo(SPI_GETFLATMENU, false)) { - result = new QPalette(menuPalette); - const QColor menubar(light ? getSysColor(COLOR_MENUBAR) : QColor(Qt::black)); - result->setColor(QPalette::Active, QPalette::Button, menubar); - result->setColor(QPalette::Disabled, QPalette::Button, menubar); - result->setColor(QPalette::Inactive, QPalette::Button, menubar); - } + if (!light || !booleanSystemParametersInfo(SPI_GETFLATMENU, false)) + return result; + + result = new QPalette(menuPalette); + const QColor menubar(getSysColor(COLOR_MENUBAR)); + result->setColor(QPalette::Active, QPalette::Button, menubar); + result->setColor(QPalette::Disabled, QPalette::Button, menubar); + result->setColor(QPalette::Inactive, QPalette::Button, menubar); return result; } @@ -447,6 +452,7 @@ QWindowsTheme *QWindowsTheme::m_instance = nullptr; QWindowsTheme::QWindowsTheme() { m_instance = this; + s_colorScheme = QWindowsTheme::queryColorScheme(); std::fill(m_fonts, m_fonts + NFonts, nullptr); std::fill(m_palettes, m_palettes + NPalettes, nullptr); refresh(); @@ -462,13 +468,16 @@ QWindowsTheme::~QWindowsTheme() static inline QStringList iconThemeSearchPaths() { - const QFileInfo appDir(QCoreApplication::applicationDirPath() + QLatin1String("/icons")); + const QFileInfo appDir(QCoreApplication::applicationDirPath() + "/icons"_L1); return appDir.isDir() ? QStringList(appDir.absoluteFilePath()) : QStringList(); } static inline QStringList styleNames() { - return { QStringLiteral("WindowsVista"), QStringLiteral("Windows") }; + QStringList styles = { QStringLiteral("WindowsVista"), QStringLiteral("Windows") }; + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) + styles.prepend(QStringLiteral("Windows11")); + return styles; } static inline int uiEffects() @@ -523,15 +532,41 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const } case MouseDoubleClickDistance: return GetSystemMetrics(SM_CXDOUBLECLK); + case MenuBarFocusOnAltPressRelease: + return true; default: break; } return QPlatformTheme::themeHint(hint); } -QPlatformTheme::Appearance QWindowsTheme::appearance() const +Qt::ColorScheme QWindowsTheme::colorScheme() const +{ + return QWindowsTheme::effectiveColorScheme(); +} + +Qt::ColorScheme QWindowsTheme::effectiveColorScheme() +{ + if (queryHighContrast()) + return Qt::ColorScheme::Unknown; + return s_colorScheme; +} + +void QWindowsTheme::handleSettingsChanged() { - return QWindowsContext::isDarkMode() ? Appearance::Dark : Appearance::Light; + const auto newColorScheme = QWindowsTheme::queryColorScheme(); + const bool colorSchemeChanged = newColorScheme != QWindowsTheme::s_colorScheme; + s_colorScheme = newColorScheme; + auto integration = QWindowsIntegration::instance(); + integration->updateApplicationBadge(); + if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) { + QWindowsTheme::instance()->refresh(); + QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>(); + } + if (colorSchemeChanged) { + for (QWindowsWindow *w : std::as_const(QWindowsContext::instance()->windows())) + w->setDarkBorder(s_colorScheme == Qt::ColorScheme::Dark); + } } void QWindowsTheme::clearPalettes() @@ -542,29 +577,64 @@ void QWindowsTheme::clearPalettes() void QWindowsTheme::refreshPalettes() { - if (!QGuiApplication::desktopSettingsAware()) return; const bool light = - !QWindowsContext::isDarkMode() + effectiveColorScheme() != Qt::ColorScheme::Dark || !QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle); - m_palettes[SystemPalette] = new QPalette(systemPalette(light)); + clearPalettes(); + m_palettes[SystemPalette] = new QPalette(QWindowsTheme::systemPalette(s_colorScheme)); m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette], light)); m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette], light)); m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light); if (!light) { - m_palettes[ButtonPalette] = new QPalette(*m_palettes[SystemPalette]); - m_palettes[ButtonPalette]->setColor(QPalette::Button, QColor(0x666666u)); - const QColor checkBoxBlue(0x0078d7u); - const QColor white(Qt::white); m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]); - m_palettes[CheckBoxPalette]->setColor(QPalette::Window, checkBoxBlue); - m_palettes[CheckBoxPalette]->setColor(QPalette::Base, checkBoxBlue); - m_palettes[CheckBoxPalette]->setColor(QPalette::Button, checkBoxBlue); - m_palettes[CheckBoxPalette]->setColor(QPalette::ButtonText, white); + m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, qt_accentColor(AccentColorNormal)); + m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, qt_accentColor(AccentColorLightest)); + m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, qt_accentColor(AccentColorDarkest)); m_palettes[RadioButtonPalette] = new QPalette(*m_palettes[CheckBoxPalette]); + } +} +QPalette QWindowsTheme::systemPalette(Qt::ColorScheme colorScheme) +{ + QPalette result = standardPalette(); + + switch (colorScheme) { + case Qt::ColorScheme::Unknown: + // when a high-contrast theme is active or when we fail to read, assume light + Q_FALLTHROUGH(); + case Qt::ColorScheme::Light: + populateLightSystemBasePalette(result); + break; + case Qt::ColorScheme::Dark: + populateDarkSystemBasePalette(result); + break; + } + + if (result.window() != result.base()) { + result.setColor(QPalette::Inactive, QPalette::Highlight, + result.color(QPalette::Inactive, QPalette::Window)); + result.setColor(QPalette::Inactive, QPalette::HighlightedText, + result.color(QPalette::Inactive, QPalette::Text)); + result.setColor(QPalette::Inactive, QPalette::Accent, + result.color(QPalette::Inactive, QPalette::Window)); } + + const QColor disabled = mixColors(result.windowText().color(), result.button().color()); + + result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(), + result.light(), result.dark(), result.mid(), + result.text(), result.brightText(), result.base(), + result.window()); + result.setColor(QPalette::Disabled, QPalette::WindowText, disabled); + result.setColor(QPalette::Disabled, QPalette::Text, disabled); + result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled); + result.setColor(QPalette::Disabled, QPalette::Highlight, result.color(QPalette::Highlight)); + result.setColor(QPalette::Disabled, QPalette::HighlightedText, result.color(QPalette::HighlightedText)); + result.setColor(QPalette::Disabled, QPalette::Accent, disabled); + result.setColor(QPalette::Disabled, QPalette::Base, result.window().color()); + return result; } void QWindowsTheme::clearFonts() @@ -606,20 +676,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)); - 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); + QWindowsContext::nonClientMetrics(&ncm, dpi); + qCDebug(lcQpaWindow) << __FUNCTION__ << ncm; + + 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); @@ -676,13 +748,9 @@ void QWindowsTheme::refreshIconPixmapSizes() fileIconSizes[LargeFileIcon] + fileIconSizes[LargeFileIcon] / 2; fileIconSizes[JumboFileIcon] = 8 * fileIconSizes[LargeFileIcon]; // empirical, has not been observed to work -#ifdef USE_IIMAGELIST int *availEnd = fileIconSizes + JumboFileIcon + 1; -#else - int *availEnd = fileIconSizes + LargeFileIcon + 1; -#endif // USE_IIMAGELIST m_fileIconSizes = QAbstractFileIconEngine::toSizeList(fileIconSizes, availEnd); - qCDebug(lcQpaWindows) << __FUNCTION__ << m_fileIconSizes; + qCDebug(lcQpaWindow) << __FUNCTION__ << m_fileIconSizes; } // Defined in qpixmap_win.cpp @@ -793,15 +861,18 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSiz } if (stockId != SIID_INVALID) { - QPixmap pixmap; SHSTOCKICONINFO iconInfo; memset(&iconInfo, 0, sizeof(iconInfo)); iconInfo.cbSize = sizeof(iconInfo); - stockFlags |= (pixmapSize.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON); - if (SHGetStockIconInfo(stockId, SHGFI_ICON | stockFlags, &iconInfo) == S_OK) { - pixmap = qt_pixmapFromWinHICON(iconInfo.hIcon); - DestroyIcon(iconInfo.hIcon); - return pixmap; + stockFlags |= SHGSI_ICONLOCATION; + if (SHGetStockIconInfo(stockId, stockFlags, &iconInfo) == S_OK) { + const auto iconSize = pixmapSize.width(); + HICON icon; + if (SHDefExtractIcon(iconInfo.szPath, iconInfo.iIcon, 0, &icon, nullptr, iconSize) == S_OK) { + QPixmap pixmap = qt_pixmapFromWinHICON(icon); + DestroyIcon(icon); + return pixmap; + } } } @@ -835,7 +906,7 @@ enum { // Shell image list ids static QString dirIconPixmapCacheKey(int iIcon, int iconSize, int imageListSize) { - QString key = QLatin1String("qt_dir_") + QString::number(iIcon); + QString key = "qt_dir_"_L1 + QString::number(iIcon); if (iconSize == SHGFI_LARGEICON) key += u'l'; switch (imageListSize) { @@ -871,10 +942,9 @@ public: // Shell image list helper functions. -static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info) +static QPixmap pixmapFromShellImageList(int iImageList, int iIcon) { QPixmap result; -#ifdef USE_IIMAGELIST // For MinGW: static const IID iID_IImageList = {0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50}}; @@ -883,16 +953,12 @@ static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info) if (hr != S_OK) return result; HICON hIcon; - hr = imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon); + hr = imageList->GetIcon(iIcon, ILD_TRANSPARENT, &hIcon); if (hr == S_OK) { result = qt_pixmapFromWinHICON(hIcon); DestroyIcon(hIcon); } imageList->Release(); -#else - Q_UNUSED(iImageList); - Q_UNUSED(info); -#endif // USE_IIMAGELIST return result; } @@ -926,19 +992,16 @@ QString QWindowsFileIconEngine::cacheKey() const || !suffix.compare(u"ico", Qt::CaseInsensitive)) { return QString(); } - return QLatin1String("qt_.") + return "qt_."_L1 + (suffix.isEmpty() ? fileInfo().fileName() : std::move(suffix).toUpper()); // handle "Makefile" ;) } QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon::State) { - /* We don't use the variable, but by storing it statically, we - * ensure CoInitialize is only called once. */ - static HRESULT comInit = CoInitialize(nullptr); - Q_UNUSED(comInit); + QComHelper comHelper; static QCache<QString, FakePointer<int> > dirIconEntryCache(1000); - static QMutex mx; + Q_CONSTINIT static QMutex mx; static int defaultFolderIIcon = -1; const bool useDefaultFolderIcon = options() & QPlatformTheme::DontUseCustomDirectoryIcons; @@ -947,13 +1010,9 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon const int width = int(size.width()); const int iconSize = width > fileIconSizes[SmallFileIcon] ? SHGFI_LARGEICON : SHGFI_SMALLICON; const int requestedImageListSize = -#ifdef USE_IIMAGELIST width > fileIconSizes[ExtraLargeFileIcon] ? sHIL_JUMBO : (width > fileIconSizes[LargeFileIcon] ? sHIL_EXTRALARGE : 0); -#else - 0; -#endif // !USE_IIMAGELIST bool cacheableDirIcon = fileInfo().isDir() && !fileInfo().isRoot(); if (cacheableDirIcon) { QMutexLocker locker(&mx); @@ -969,7 +1028,6 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon } } - SHFILEINFO info; unsigned int flags = SHGFI_ICON | iconSize | SHGFI_SYSICONINDEX | SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX; DWORD attributes = 0; QString path = filePath; @@ -981,43 +1039,43 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon flags |= SHGFI_USEFILEATTRIBUTES; attributes |= FILE_ATTRIBUTE_NORMAL; } - const bool val = shGetFileInfoBackground(path, attributes, &info, flags); - + auto task = QSharedPointer<QShGetFileInfoThread::Task>( + new QShGetFileInfoThread::Task(path, attributes, flags)); + s_shGetFileInfoThread()->runWithParams(task); // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases - if (val && info.hIcon) { + if (task->resultValid()) { QString key; if (cacheableDirIcon) { if (useDefaultFolderIcon && defaultFolderIIcon < 0) - defaultFolderIIcon = info.iIcon; + defaultFolderIIcon = task->iIcon; //using the unique icon index provided by windows save us from duplicate keys - key = dirIconPixmapCacheKey(info.iIcon, iconSize, requestedImageListSize); + key = dirIconPixmapCacheKey(task->iIcon, iconSize, requestedImageListSize); QPixmapCache::find(key, &pixmap); if (!pixmap.isNull()) { QMutexLocker locker(&mx); - dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon)); + dirIconEntryCache.insert(filePath, FakePointer<int>::create(task->iIcon)); } } if (pixmap.isNull()) { if (requestedImageListSize) { - pixmap = pixmapFromShellImageList(requestedImageListSize, info); + pixmap = pixmapFromShellImageList(requestedImageListSize, task->iIcon); if (pixmap.isNull() && requestedImageListSize == sHIL_JUMBO) - pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, info); + pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, task->iIcon); } if (pixmap.isNull()) - pixmap = qt_pixmapFromWinHICON(info.hIcon); + pixmap = qt_pixmapFromWinHICON(task->hIcon); if (!pixmap.isNull()) { if (cacheableDirIcon) { QMutexLocker locker(&mx); QPixmapCache::insert(key, pixmap); - dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon)); + dirIconEntryCache.insert(filePath, FakePointer<int>::create(task->iIcon)); } } else { qWarning("QWindowsTheme::fileIconPixmap() no icon found"); } } - DestroyIcon(info.hIcon); } return pixmap; @@ -1028,6 +1086,11 @@ QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOpt return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions)); } +QIconEngine *QWindowsTheme::createIconEngine(const QString &iconName) const +{ + return new QWindowsIconEngine(iconName); +} + static inline bool doUseNativeMenus() { const unsigned options = QWindowsIntegration::instance()->options(); @@ -1052,21 +1115,23 @@ bool QWindowsTheme::useNativeMenus() return result; } -bool QWindowsTheme::queryDarkMode() +Qt::ColorScheme QWindowsTheme::queryColorScheme() { - if (QOperatingSystemVersion::current() - < QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 17763) - || queryHighContrast()) { - return false; - } + if (queryHighContrast()) + return Qt::ColorScheme::Unknown; + const auto setting = QWinRegistryKey(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)") .dwordValue(L"AppsUseLightTheme"); - return setting.second && setting.first == 0; + return setting.second && setting.first == 0 ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; } bool QWindowsTheme::queryHighContrast() { - return booleanSystemParametersInfo(SPI_GETHIGHCONTRAST, false); + HIGHCONTRAST hcf = {}; + hcf.cbSize = static_cast<UINT>(sizeof(HIGHCONTRAST)); + if (SystemParametersInfo(SPI_GETHIGHCONTRAST, hcf.cbSize, &hcf, FALSE)) + return hcf.dwFlags & HCF_HIGHCONTRASTON; + return false; } QPlatformMenuItem *QWindowsTheme::createPlatformMenuItem() const diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h index 9d5fcc92fe..6109122944 100644 --- a/src/plugins/platforms/windows/qwindowstheme.h +++ b/src/plugins/platforms/windows/qwindowstheme.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSTHEME_H #define QWINDOWSTHEME_H @@ -44,6 +8,8 @@ #include <QtCore/qsharedpointer.h> #include <QtCore/qvariant.h> +#include <QtCore/qlist.h> +#include <QtCore/qsize.h> QT_BEGIN_NAMESPACE @@ -65,7 +31,9 @@ public: #endif QVariant themeHint(ThemeHint) const override; - Appearance appearance() const override; + Qt::ColorScheme colorScheme() const override; + + static void handleSettingsChanged(); const QPalette *palette(Palette type = SystemPalette) const override { return m_palettes[type]; } @@ -75,6 +43,7 @@ public: QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override; QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions = {}) const override; + QIconEngine *createIconEngine(const QString &iconName) const override; void windowsThemeChanged(QWindow *window); void displayChanged() { refreshIconPixmapSizes(); } @@ -87,21 +56,29 @@ public: void showPlatformMenuBar() override; static bool useNativeMenus(); - static bool queryDarkMode(); - static bool queryHighContrast(); void refreshFonts(); void refresh(); static const char *name; + static QPalette systemPalette(Qt::ColorScheme); + private: void clearPalettes(); void refreshPalettes(); void clearFonts(); void refreshIconPixmapSizes(); + static void populateLightSystemBasePalette(QPalette &result); + static void populateDarkSystemBasePalette(QPalette &result); + + static Qt::ColorScheme queryColorScheme(); + static Qt::ColorScheme effectiveColorScheme(); + static bool queryHighContrast(); + static QWindowsTheme *m_instance; + static inline Qt::ColorScheme s_colorScheme = Qt::ColorScheme::Unknown; QPalette *m_palettes[NPalettes]; QFont *m_fonts[NFonts]; QList<QSize> m_fileIconSizes; diff --git a/src/plugins/platforms/windows/qwindowsthreadpoolrunner.h b/src/plugins/platforms/windows/qwindowsthreadpoolrunner.h index a31e00c0ac..cc550d912b 100644 --- a/src/plugins/platforms/windows/qwindowsthreadpoolrunner.h +++ b/src/plugins/platforms/windows/qwindowsthreadpoolrunner.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSTHREADPOOLRUNNER_H #define QWINDOWSTHREADPOOLRUNNER_H diff --git a/src/plugins/platforms/windows/qwindowsvulkaninstance.cpp b/src/plugins/platforms/windows/qwindowsvulkaninstance.cpp index 812ea8193a..0c2ee9edf5 100644 --- a/src/plugins/platforms/windows/qwindowsvulkaninstance.cpp +++ b/src/plugins/platforms/windows/qwindowsvulkaninstance.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsvulkaninstance.h" diff --git a/src/plugins/platforms/windows/qwindowsvulkaninstance.h b/src/plugins/platforms/windows/qwindowsvulkaninstance.h index 4b9293cc64..5e287d0302 100644 --- a/src/plugins/platforms/windows/qwindowsvulkaninstance.h +++ b/src/plugins/platforms/windows/qwindowsvulkaninstance.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSVULKANINSTANCE_H #define QWINDOWSVULKANINSTANCE_H diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index a65ac482b2..5d96d40af5 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1,51 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#if defined(WINVER) && WINVER < 0x0601 -# undef WINVER -#endif -#if !defined(WINVER) -# define WINVER 0x0601 // Enable touch functions for MinGW -#endif +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtCore/qt_windows.h> #include "qwindowswindow.h" #include "qwindowscontext.h" +#include "qwindowstheme.h" #if QT_CONFIG(draganddrop) # include "qwindowsdrag.h" #endif @@ -68,7 +28,7 @@ #include <QtGui/qwindow.h> #include <QtGui/qregion.h> #include <QtGui/qopenglcontext.h> -#include <private/qsystemlibrary_p.h> +#include <QtGui/private/qwindowsthemecache_p.h> #include <private/qwindow_p.h> // QWINDOWSIZE_MAX #include <private/qguiapplication_p.h> #include <private/qhighdpiscaling_p.h> @@ -76,7 +36,6 @@ #include <QtCore/qdebug.h> #include <QtCore/qlibraryinfo.h> -#include <QtCore/qoperatingsystemversion.h> #include <dwmapi.h> @@ -84,6 +43,8 @@ #include "qwindowsvulkaninstance.h" #endif +#include <shellscalingapi.h> + QT_BEGIN_NAMESPACE using QWindowCreationContextPtr = QSharedPointer<QWindowCreationContext>; @@ -119,6 +80,34 @@ static QByteArray debugWinStyle(DWORD style) rc += " WS_MINIMIZEBOX"; if (style & WS_MAXIMIZEBOX) rc += " WS_MAXIMIZEBOX"; + if (style & WS_BORDER) + rc += " WS_BORDER"; + if (style & WS_CAPTION) + rc += " WS_CAPTION"; + if (style & WS_CHILDWINDOW) + rc += " WS_CHILDWINDOW"; + if (style & WS_DISABLED) + rc += " WS_DISABLED"; + if (style & WS_GROUP) + rc += " WS_GROUP"; + if (style & WS_HSCROLL) + rc += " WS_HSCROLL"; + if (style & WS_ICONIC) + rc += " WS_ICONIC"; + if (style & WS_MAXIMIZE) + rc += " WS_MAXIMIZE"; + if (style & WS_MINIMIZE) + rc += " WS_MINIMIZE"; + if (style & WS_SIZEBOX) + rc += " WS_SIZEBOX"; + if (style & WS_TABSTOP) + rc += " WS_TABSTOP"; + if (style & WS_TILED) + rc += " WS_TILED"; + if (style & WS_VISIBLE) + rc += " WS_VISIBLE"; + if (style & WS_VSCROLL) + rc += " WS_VSCROLL"; return rc; } @@ -138,6 +127,44 @@ static QByteArray debugWinExStyle(DWORD exStyle) rc += " WS_EX_LAYOUTRTL"; if (exStyle & WS_EX_NOINHERITLAYOUT) rc += " WS_EX_NOINHERITLAYOUT"; + if (exStyle & WS_EX_ACCEPTFILES) + rc += " WS_EX_ACCEPTFILES"; + if (exStyle & WS_EX_APPWINDOW) + rc += " WS_EX_APPWINDOW"; + if (exStyle & WS_EX_CLIENTEDGE) + rc += " WS_EX_CLIENTEDGE"; + if (exStyle & WS_EX_COMPOSITED) + rc += " WS_EX_COMPOSITED"; + if (exStyle & WS_EX_CONTROLPARENT) + rc += " WS_EX_CONTROLPARENT"; + if (exStyle & WS_EX_LEFT) + rc += " WS_EX_LEFT"; + if (exStyle & WS_EX_LEFTSCROLLBAR) + rc += " WS_EX_LEFTSCROLLBAR"; + if (exStyle & WS_EX_LTRREADING) + rc += " WS_EX_LTRREADING"; + if (exStyle & WS_EX_MDICHILD) + rc += " WS_EX_MDICHILD"; + if (exStyle & WS_EX_NOACTIVATE) + rc += " WS_EX_NOACTIVATE"; + if (exStyle & WS_EX_NOPARENTNOTIFY) + rc += " WS_EX_NOPARENTNOTIFY"; + if (exStyle & WS_EX_NOREDIRECTIONBITMAP) + rc += " WS_EX_NOREDIRECTIONBITMAP"; + if (exStyle & WS_EX_RIGHT) + rc += " WS_EX_RIGHT"; + if (exStyle & WS_EX_RIGHTSCROLLBAR) + rc += " WS_EX_RIGHTSCROLLBAR"; + if (exStyle & WS_EX_RTLREADING) + rc += " WS_EX_RTLREADING"; + if (exStyle & WS_EX_STATICEDGE) + rc += " WS_EX_STATICEDGE"; + if (exStyle & WS_EX_TOPMOST) + rc += " WS_EX_TOPMOST"; + if (exStyle & WS_EX_TRANSPARENT) + rc += " WS_EX_TRANSPARENT"; + if (exStyle & WS_EX_WINDOWEDGE) + rc += " WS_EX_WINDOWEDGE"; return rc; } @@ -167,6 +194,62 @@ static QByteArray debugWinSwpPos(UINT flags) rc += " SWP_NOZORDER"; if (flags & SWP_SHOWWINDOW) rc += " SWP_SHOWWINDOW"; + if (flags & SWP_ASYNCWINDOWPOS) + rc += " SWP_ASYNCWINDOWPOS"; + if (flags & SWP_DEFERERASE) + rc += " SWP_DEFERERASE"; + if (flags & SWP_DRAWFRAME) + rc += " SWP_DRAWFRAME"; + if (flags & SWP_NOREPOSITION) + rc += " SWP_NOREPOSITION"; + return rc; +} + +[[nodiscard]] static inline QByteArray debugWindowPlacementFlags(const UINT flags) +{ + QByteArray rc = "0x"; + rc += QByteArray::number(flags, 16); + if (flags & WPF_SETMINPOSITION) + rc += " WPF_SETMINPOSITION"; + if (flags & WPF_RESTORETOMAXIMIZED) + rc += " WPF_RESTORETOMAXIMIZED"; + if (flags & WPF_ASYNCWINDOWPLACEMENT) + rc += " WPF_ASYNCWINDOWPLACEMENT"; + return rc; +} + +[[nodiscard]] static inline QByteArray debugShowWindowCmd(const UINT cmd) +{ + QByteArray rc = {}; + rc += QByteArray::number(cmd); + if (cmd == SW_HIDE) + rc += " SW_HIDE"; + if (cmd == SW_SHOWNORMAL) + rc += " SW_SHOWNORMAL"; + if (cmd == SW_NORMAL) + rc += " SW_NORMAL"; + if (cmd == SW_SHOWMINIMIZED) + rc += " SW_SHOWMINIMIZED"; + if (cmd == SW_SHOWMAXIMIZED) + rc += " SW_SHOWMAXIMIZED"; + if (cmd == SW_MAXIMIZE) + rc += " SW_MAXIMIZE"; + if (cmd == SW_SHOWNOACTIVATE) + rc += " SW_SHOWNOACTIVATE"; + if (cmd == SW_SHOW) + rc += " SW_SHOW"; + if (cmd == SW_MINIMIZE) + rc += " SW_MINIMIZE"; + if (cmd == SW_SHOWMINNOACTIVE) + rc += " SW_SHOWMINNOACTIVE"; + if (cmd == SW_SHOWNA) + rc += " SW_SHOWNA"; + if (cmd == SW_RESTORE) + rc += " SW_RESTORE"; + if (cmd == SW_SHOWDEFAULT) + rc += " SW_SHOWDEFAULT"; + if (cmd == SW_FORCEMINIMIZE) + rc += " SW_FORCEMINIMIZE"; return rc; } @@ -202,7 +285,9 @@ QDebug operator<<(QDebug d, const RECT &r) QDebug operator<<(QDebug d, const POINT &p) { - d << p.x << ',' << p.y; + QDebugStateSaver saver(d); + d.nospace(); + d << "POINT(x=" << p.x << ", y=" << p.y << ')'; return d; } @@ -221,7 +306,7 @@ QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p) { QDebugStateSaver saver(d); d.nospace(); - d << "NCCALCSIZE_PARAMS(rgrc=[" << p.rgrc[0] << ' ' << p.rgrc[1] << ' ' + d << "NCCALCSIZE_PARAMS(rgrc=[" << p.rgrc[0] << ", " << p.rgrc[1] << ", " << p.rgrc[2] << "], lppos=" << *p.lppos << ')'; return d; } @@ -230,11 +315,10 @@ QDebug operator<<(QDebug d, const MINMAXINFO &i) { QDebugStateSaver saver(d); d.nospace(); - d << "MINMAXINFO maxSize=" << i.ptMaxSize.x << ',' - << i.ptMaxSize.y << " maxpos=" << i.ptMaxPosition.x - << ',' << i.ptMaxPosition.y << " mintrack=" - << i.ptMinTrackSize.x << ',' << i.ptMinTrackSize.y - << " maxtrack=" << i.ptMaxTrackSize.x << ',' << i.ptMaxTrackSize.y; + d << "MINMAXINFO(maxSize=" << i.ptMaxSize << ", " + << "maxpos=" << i.ptMaxPosition << ", " + << "maxtrack=" << i.ptMaxTrackSize << ", " + << "mintrack=" << i.ptMinTrackSize << ')'; return d; } @@ -243,9 +327,10 @@ QDebug operator<<(QDebug d, const WINDOWPLACEMENT &wp) QDebugStateSaver saver(d); d.nospace(); d.noquote(); - d << "WINDOWPLACEMENT(flags=0x" << Qt::hex << wp.flags << Qt::dec << ", showCmd=" - << wp.showCmd << ", ptMinPosition=" << wp.ptMinPosition << ", ptMaxPosition=" << wp.ptMaxPosition - << ", rcNormalPosition=" << wp.rcNormalPosition; + d << "WINDOWPLACEMENT(flags=" << debugWindowPlacementFlags(wp.flags) << ", showCmd=" + << debugShowWindowCmd(wp.showCmd) << ", ptMinPosition=" << wp.ptMinPosition + << ", ptMaxPosition=" << wp.ptMaxPosition << ", rcNormalPosition=" + << wp.rcNormalPosition << ')'; return d; } @@ -346,11 +431,7 @@ static inline bool windowIsAccelerated(const QWindow *w) { switch (w->surfaceType()) { case QSurface::OpenGLSurface: - return true; - case QSurface::RasterGLSurface: - return qt_window_private(const_cast<QWindow *>(w))->compositing; case QSurface::VulkanSurface: - return true; case QSurface::Direct3DSurface: return true; default: @@ -385,20 +466,27 @@ static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags) w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX); } +bool QWindowsWindow::hasNoNativeFrame(HWND hwnd, Qt::WindowFlags flags) +{ + const LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE); + return (style & WS_CHILD) || (flags & Qt::FramelessWindowHint); +} + // Set the WS_EX_LAYERED flag on a HWND if required. This is required for // translucent backgrounds, not fully opaque windows and for // Qt::WindowTransparentForInput (in combination with WS_EX_TRANSPARENT). bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity) { - const LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + const LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + // Native children are frameless by nature, so check for that as well. const bool needsLayered = (flags & Qt::WindowTransparentForInput) - || (hasAlpha && (flags & Qt::FramelessWindowHint)) || opacity < 1.0; + || (hasAlpha && hasNoNativeFrame(hwnd, flags)) || opacity < 1.0; const bool isLayered = (exStyle & WS_EX_LAYERED); if (needsLayered != isLayered) { if (needsLayered) { - SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED); + SetWindowLongPtr(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED); } else { - SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED); + SetWindowLongPtr(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED); } } return needsLayered; @@ -408,7 +496,7 @@ static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bo { if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) { const BYTE alpha = BYTE(qRound(255.0 * level)); - if (hasAlpha && !accelerated && (flags & Qt::FramelessWindowHint)) { + if (hasAlpha && !accelerated && QWindowsWindow::hasNoNativeFrame(hwnd, flags)) { // Non-GL windows with alpha: Use blend function to update. BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; UpdateLayeredWindow(hwnd, nullptr, nullptr, nullptr, nullptr, nullptr, 0, &blend, ULW_ALPHA); @@ -431,31 +519,41 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo setWindowOpacity(hwnd, flags, hasAlpha, isAccelerated, opacity); } +[[nodiscard]] static inline int getResizeBorderThickness(const UINT dpi) +{ + // The width of the padded border will always be 0 if DWM composition is + // disabled, but since it will always be enabled and can't be programtically + // disabled from Windows 8, we are safe to go. + return GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); +} + /*! Calculates the dimensions of the invisible borders within the - window frames in Windows 10, using an empirical expression that - reproduces the measured values for standard DPI settings. + window frames which only exist on Windows 10 and onwards. */ static QMargins invisibleMargins(QPoint screenPoint) { - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) { - POINT pt = {screenPoint.x(), screenPoint.y()}; - if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) { - if (QWindowsContext::shcoredll.isValid()) { - UINT dpiX; - UINT dpiY; - if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY))) { - const qreal sc = (dpiX - 96) / 96.0; - const int gap = 7 + qRound(5*sc) - int(sc); - return QMargins(gap, 0, gap, gap); - } - } + POINT pt = {screenPoint.x(), screenPoint.y()}; + if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) { + UINT dpiX; + UINT dpiY; + if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { + const int gap = getResizeBorderThickness(dpiX); + return QMargins(gap, 0, gap, gap); } } return QMargins(); } +[[nodiscard]] static inline QMargins invisibleMargins(const HWND hwnd) +{ + const UINT dpi = GetDpiForWindow(hwnd); + const int gap = getResizeBorderThickness(dpi); + return QMargins(gap, 0, gap, gap); +} + /*! \class WindowCreationData \brief Window creation code. @@ -545,13 +643,18 @@ static inline void fixTopLevelWindowFlags(Qt::WindowFlags &flags) flags |= Qt::FramelessWindowHint; } -static QScreen *screenForName(const QWindow *w, const QString &name) +static QScreen *screenForDeviceName(const QWindow *w, const QString &name) { + const auto getDeviceName = [](const QScreen *screen) -> QString { + if (const auto s = static_cast<const QWindowsScreen *>(screen->handle())) + return s->data().deviceName; + return {}; + }; QScreen *winScreen = w ? w->screen() : QGuiApplication::primaryScreen(); - if (winScreen && winScreen->name() != name) { + if (winScreen && getDeviceName(winScreen) != name) { const auto screens = winScreen->virtualSiblings(); for (QScreen *screen : screens) { - if (screen->name() == name) + if (getDeviceName(screen) == name) return screen; } } @@ -562,7 +665,7 @@ static QPoint calcPosition(const QWindow *w, const QWindowCreationContextPtr &co { const QPoint orgPos(context->frameX - invMargins.left(), context->frameY - invMargins.top()); - if (!w || (!w->isTopLevel() && w->surfaceType() != QWindow::OpenGLSurface)) + if (!w || w->type() != Qt::Window) return orgPos; // Workaround for QTBUG-50371 @@ -695,55 +798,68 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag style = WS_CHILD; } - // if (!testAttribute(Qt::WA_PaintUnclipped)) - // ### Commented out for now as it causes some problems, but - // this should be correct anyway, so dig some more into this -#ifdef Q_FLATTEN_EXPOSE - if (windowIsOpenGL(w)) // a bit incorrect since the is-opengl status may change from false to true at any time later on - style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // see SetPixelFormat -#else - style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ; -#endif - if (topLevel) { - if ((type == Qt::Window || dialog || tool)) { - if (!(flags & Qt::FramelessWindowHint)) { - style |= WS_POPUP; - if (flags & Qt::MSWindowsFixedSizeDialogHint) { - style |= WS_DLGFRAME; - } else { - style |= WS_THICKFRAME; - } - if (flags & Qt::WindowTitleHint) - style |= WS_CAPTION; // Contains WS_DLGFRAME - } - if (flags & Qt::WindowSystemMenuHint) - style |= WS_SYSMENU; - else if (dialog && (flags & Qt::WindowCloseButtonHint) && !(flags & Qt::FramelessWindowHint)) { - style |= WS_SYSMENU | WS_BORDER; // QTBUG-2027, dialogs without system menu. - exStyle |= WS_EX_DLGMODALFRAME; + style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ; + + if (topLevel) { + if ((type == Qt::Window || dialog || tool)) { + if (!(flags & Qt::FramelessWindowHint)) { + style |= WS_POPUP; + if (flags & Qt::MSWindowsFixedSizeDialogHint) { + style |= WS_DLGFRAME; + } else { + style |= WS_THICKFRAME; } - if (flags & Qt::WindowMinimizeButtonHint) - style |= WS_MINIMIZEBOX; - if (shouldShowMaximizeButton(w, flags)) - style |= WS_MAXIMIZEBOX; - if (tool) - exStyle |= WS_EX_TOOLWINDOW; - if (flags & Qt::WindowContextHelpButtonHint) - exStyle |= WS_EX_CONTEXTHELP; - } else { - exStyle |= WS_EX_TOOLWINDOW; + if (flags & Qt::WindowTitleHint) + style |= WS_CAPTION; // Contains WS_DLGFRAME } + if (flags & Qt::WindowSystemMenuHint) + style |= WS_SYSMENU; + else if (dialog && (flags & Qt::WindowCloseButtonHint) && !(flags & Qt::FramelessWindowHint)) { + style |= WS_SYSMENU | WS_BORDER; // QTBUG-2027, dialogs without system menu. + exStyle |= WS_EX_DLGMODALFRAME; + } + const bool showMinimizeButton = flags & Qt::WindowMinimizeButtonHint; + if (showMinimizeButton) + style |= WS_MINIMIZEBOX; + const bool showMaximizeButton = shouldShowMaximizeButton(w, flags); + if (showMaximizeButton) + style |= WS_MAXIMIZEBOX; + if (showMinimizeButton || showMaximizeButton) + style |= WS_SYSMENU; + if (tool) + exStyle |= WS_EX_TOOLWINDOW; + if ((flags & Qt::WindowContextHelpButtonHint) && !showMinimizeButton + && !showMaximizeButton) + exStyle |= WS_EX_CONTEXTHELP; + } else { + exStyle |= WS_EX_TOOLWINDOW; + } - // make mouse events fall through this window - // NOTE: WS_EX_TRANSPARENT flag can make mouse inputs fall through a layered window - if (flagsIn & Qt::WindowTransparentForInput) - exStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT; + // make mouse events fall through this window + // NOTE: WS_EX_TRANSPARENT flag can make mouse inputs fall through a layered window + if (flagsIn & Qt::WindowTransparentForInput) + exStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT; + + // Currently only compatible with D3D surfaces, use it with care. + if (qEnvironmentVariableIntValue("QT_QPA_DISABLE_REDIRECTION_SURFACE")) + exStyle |= WS_EX_NOREDIRECTIONBITMAP; } } static inline bool shouldApplyDarkFrame(const QWindow *w) { - return w->isTopLevel() && !w->flags().testFlag(Qt::FramelessWindowHint); + if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint)) + return false; + // 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. + auto *dWindow = QWindowPrivate::get(const_cast<QWindow*>(w)); + const QPalette windowPal = dWindow->windowPalette(); + return windowPal.color(QPalette::WindowText).lightness() + > windowPal.color(QPalette::Window).lightness(); } QWindowsWindowData @@ -774,11 +890,12 @@ 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(); - qCDebug(lcQpaWindows).nospace() + qCDebug(lcQpaWindow).nospace() << "CreateWindowEx: " << w << " class=" << windowClassName << " title=" << title << '\n' << *this << "\nrequested: " << rect << ": " << context->frameWidth << 'x' << context->frameHeight @@ -804,7 +921,7 @@ QWindowsWindowData pos.x(), pos.y(), context->frameWidth, context->frameHeight, parentHandle, nullptr, appinst, nullptr); - qCDebug(lcQpaWindows).nospace() + qCDebug(lcQpaWindow).nospace() << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: " << context->obtainedPos << context->obtainedSize << ' ' << context->margins; @@ -813,11 +930,8 @@ QWindowsWindowData return result; } - if (QWindowsContext::isDarkMode() - && QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames) - && shouldApplyDarkFrame(w)) { + if (QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark && shouldApplyDarkFrame(w)) QWindowsWindow::setDarkBorderToWindow(result.hwnd, true); - } if (mirrorParentWidth != 0) { context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width() @@ -827,6 +941,7 @@ QWindowsWindowData QRect obtainedGeometry(context->obtainedPos, context->obtainedSize); result.geometry = obtainedGeometry; + result.restoreGeometry = frameGeometry(result.hwnd, topLevel); result.fullFrameMargins = context->margins; result.embedded = embedded; result.hasFrame = hasFrame; @@ -847,7 +962,7 @@ void WindowCreationData::applyWindowFlags(HWND hwnd) const const LONG_PTR newExStyle = exStyle; if (newExStyle != oldExStyle) SetWindowLongPtr(hwnd, GWL_EXSTYLE, newExStyle); - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << hwnd << *this + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << hwnd << *this << "\n Style from " << debugWinStyle(DWORD(oldStyle)) << "\n to " << debugWinStyle(DWORD(newStyle)) << "\n ExStyle from " << debugWinExStyle(DWORD(oldExStyle)) << " to " @@ -901,6 +1016,21 @@ static QSize toNativeSizeConstrained(QSize dip, const QScreen *s) return dip; } +// Helper for checking if frame adjustment needs to be skipped +// NOTE: Unmaximized frameless windows will skip margins calculation +static bool shouldOmitFrameAdjustment(const Qt::WindowFlags flags, DWORD style) +{ + return flags.testFlag(Qt::FramelessWindowHint) && !(style & WS_MAXIMIZE); +} + +// Helper for checking if frame adjustment needs to be skipped +// NOTE: Unmaximized frameless windows will skip margins calculation +static bool shouldOmitFrameAdjustment(const Qt::WindowFlags flags, HWND hwnd) +{ + DWORD style = hwnd != nullptr ? DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)) : 0; + return flags.testFlag(Qt::FramelessWindowHint) && !(style & WS_MAXIMIZE); +} + /*! \class QWindowsGeometryHint \brief Stores geometry constraints and provides utility functions. @@ -911,76 +1041,84 @@ static QSize toNativeSizeConstrained(QSize dip, const QScreen *s) \internal */ -QMargins QWindowsGeometryHint::frameOnPrimaryScreen(DWORD style, DWORD exStyle) +QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, DWORD style, DWORD exStyle) { + if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style)) + return {}; RECT rect = {0,0,0,0}; style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE) qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << " style=" << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase << ' ' << rect << ' ' << result; return result; } -QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd) +QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, HWND hwnd) { - return frameOnPrimaryScreen(DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)), + return frameOnPrimaryScreen(w, DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)), DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE))); } -QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle, qreal dpi) +QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyle, qreal dpi) { - if (QWindowsContext::user32dll.adjustWindowRectExForDpi == nullptr) - return frameOnPrimaryScreen(style, exStyle); + if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style)) + return {}; RECT rect = {0,0,0,0}; style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. - if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle, - unsigned(qRound(dpi))) == FALSE) { + if (AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) { qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__); } const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << " style=" << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase << " dpi=" << dpi << ' ' << rect << ' ' << result; return result; } -QMargins QWindowsGeometryHint::frame(HWND hwnd, DWORD style, DWORD exStyle) +QMargins QWindowsGeometryHint::frame(const QWindow *w, HWND hwnd, DWORD style, DWORD exStyle) { + if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style)) + return {}; if (QWindowsScreenManager::isSingleScreen()) - return frameOnPrimaryScreen(style, exStyle); - auto screenManager = QWindowsContext::instance()->screenManager(); + return frameOnPrimaryScreen(w, style, exStyle); + auto &screenManager = QWindowsContext::instance()->screenManager(); auto screen = screenManager.screenForHwnd(hwnd); if (!screen) screen = screenManager.screens().value(0); const auto dpi = screen ? screen->logicalDpi().first : qreal(96); - return frame(style, exStyle, dpi); + return frame(w, style, exStyle, dpi); +} + +QMargins QWindowsGeometryHint::frame(const QWindow *w, HWND hwnd) +{ + return frame(w, hwnd, DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)), + DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE))); } // For newly created windows. QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry, DWORD style, DWORD exStyle) { - if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint)) + if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style)) return {}; - if (!QWindowsContext::user32dll.adjustWindowRectExForDpi - || QWindowsScreenManager::isSingleScreen() + if (QWindowsScreenManager::isSingleScreen() || !QWindowsContext::shouldHaveNonClientDpiScaling(w)) { - return frameOnPrimaryScreen(style, exStyle); + return frameOnPrimaryScreen(w, style, exStyle); } qreal dpi = 96; - auto screenManager = QWindowsContext::instance()->screenManager(); + auto &screenManager = QWindowsContext::instance()->screenManager(); auto screen = screenManager.screenAtDp(geometry.center()); if (!screen) screen = screenManager.screens().value(0); if (screen) dpi = screen->logicalDpi().first; - return QWindowsGeometryHint::frame(style, exStyle, dpi); + return QWindowsGeometryHint::frame(w, style, exStyle, dpi); } bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result) @@ -996,7 +1134,7 @@ bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, co ncp->rgrc[0].right -= customMargins.right(); ncp->rgrc[0].bottom -= customMargins.bottom(); result = nullptr; - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2] << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy; return true; @@ -1032,7 +1170,7 @@ void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w, QSize minimumSize; QSize maximumSize; frameSizeConstraints(w, screen, margins, &minimumSize, &maximumSize); - qCDebug(lcQpaWindows).nospace() << '>' << __FUNCTION__ << '<' << " min=" + qCDebug(lcQpaWindow).nospace() << '>' << __FUNCTION__ << '<' << " min=" << minimumSize.width() << ',' << minimumSize.height() << " max=" << maximumSize.width() << ',' << maximumSize.height() << " margins=" << margins @@ -1047,7 +1185,7 @@ void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w, mmi->ptMaxTrackSize.x = maximumSize.width(); if (maximumSize.height() < QWINDOWSIZE_MAX) mmi->ptMaxTrackSize.y = maximumSize.height(); - qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ << " out " << *mmi; + qCDebug(lcQpaWindow).nospace() << '<' << __FUNCTION__ << " out " << *mmi; } void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w, @@ -1082,7 +1220,7 @@ bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) bool QWindowsBaseWindow::isRtlLayout(HWND hwnd) { - return (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0; + return (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0; } QWindowsBaseWindow *QWindowsBaseWindow::baseWindowOf(const QWindow *w) @@ -1118,7 +1256,7 @@ QRect QWindowsBaseWindow::geometry_sys() const QMargins QWindowsBaseWindow::frameMargins_sys() const { - return QWindowsGeometryHint::frame(handle(), style(), exStyle()); + return QWindowsGeometryHint::frame(window(), handle(), style(), exStyle()); } std::optional<QWindowsBaseWindow::TouchWindowTouchTypes> @@ -1143,7 +1281,7 @@ void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other window void QWindowsBaseWindow::raise_sys() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window(); const Qt::WindowType type = window()->type(); if (type == Qt::Popup || type == Qt::SubWindow // Special case for QTBUG-63121: MDI subwindows with WindowStaysOnTopHint @@ -1154,14 +1292,14 @@ void QWindowsBaseWindow::raise_sys() void QWindowsBaseWindow::lower_sys() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window(); if (!(window()->flags() & Qt::WindowStaysOnTopHint)) SetWindowPos(handle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } void QWindowsBaseWindow::setWindowTitle_sys(const QString &title) { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << title; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << title; SetWindowText(handle(), reinterpret_cast<const wchar_t *>(title.utf16())); } @@ -1220,6 +1358,8 @@ QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd) , m_hwnd(hwnd) , m_topLevelStyle(0) { + if (QPlatformWindow::parent()) + setParent(QPlatformWindow::parent()); } void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow) @@ -1228,7 +1368,7 @@ void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow) const HWND newParent = newParentWindow ? reinterpret_cast<HWND>(newParentWindow->winId()) : HWND(nullptr); const bool isTopLevel = !newParent; const DWORD oldStyle = style(); - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << "newParent=" + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << "newParent=" << newParentWindow << newParent << "oldStyle=" << debugWinStyle(oldStyle); SetParent(m_hwnd, newParent); if (wasTopLevel != isTopLevel) { // Top level window flags need to be set/cleared manually. @@ -1246,7 +1386,7 @@ void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow) void QWindowsForeignWindow::setVisible(bool visible) { - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << visible; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << visible; if (visible) ShowWindow(handle(), SW_SHOWNOACTIVATE); else @@ -1283,13 +1423,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(); @@ -1308,7 +1451,7 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QScreen * } } - qCDebug(lcQpaWindows).nospace() + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << ' ' << w << ' ' << geometry << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w) << " frame=" << frameWidth << 'x' << frameHeight << '+' @@ -1328,7 +1471,7 @@ void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const \list \li Raster type: handleWmPaint() is implemented to to bitblt the image. The DC can be accessed - via getDC/Relase DC, which has a special handling + via getDC/releaseDC, which has special handling when within a paint event (in that case, the DC obtained from BeginPaint() is returned). @@ -1345,6 +1488,7 @@ void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const const char *QWindowsWindow::embeddedNativeParentHandleProperty = "_q_embedded_native_parent_handle"; const char *QWindowsWindow::hasBorderInFullScreenProperty = "_q_has_border_in_fullscreen"; bool QWindowsWindow::m_borderInFullScreenDefault = false; +bool QWindowsWindow::m_inSetgeometry = false; QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) : QWindowsBaseWindow(aWindow), @@ -1391,6 +1535,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) QWindowsWindow::~QWindowsWindow() { setFlag(WithinDestroy); + QWindowsThemeCache::clearThemeCache(m_data.hwnd); if (testFlag(TouchRegistered)) UnregisterTouchWindow(m_data.hwnd); destroyWindow(); @@ -1411,16 +1556,15 @@ void QWindowsWindow::initialize() if (w->type() != Qt::Desktop) { const Qt::WindowState state = w->windowState(); const QRect obtainedGeometry(creationContext->obtainedPos, creationContext->obtainedSize); + QPlatformScreen *obtainedScreen = screenForGeometry(obtainedGeometry); + if (obtainedScreen && screen() != obtainedScreen) + QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen()); if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen && creationContext->requestedGeometryIn != obtainedGeometry) { QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry); } - QPlatformScreen *obtainedScreen = screenForGeometry(obtainedGeometry); - if (obtainedScreen && screen() != obtainedScreen) - QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen()); } - QWindowsWindow::setSavedDpi(QWindowsContext::user32dll.getDpiForWindow ? - QWindowsContext::user32dll.getDpiForWindow(handle()) : 96); + QWindowsWindow::setSavedDpi(GetDpiForWindow(handle())); } QSurfaceFormat QWindowsWindow::format() const @@ -1444,7 +1588,7 @@ void QWindowsWindow::fireFullExpose(bool force) void QWindowsWindow::destroyWindow() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << m_data.hwnd; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << m_data.hwnd; if (m_data.hwnd) { // Stop event dispatching before Window is destroyed. setFlag(WithinDestroy); // Clear any transient child relationships as Windows will otherwise destroy them (QTBUG-35499, QTBUG-36666) @@ -1551,7 +1695,7 @@ QScreen *QWindowsWindow::forcedScreenForGLWindow(const QWindow *w) forceToScreen = GpuDescription::detect().gpuSuitableScreen; m_screenForGLInitialized = true; } - return forceToScreen.isEmpty() ? nullptr : screenForName(w, forceToScreen); + return forceToScreen.isEmpty() ? nullptr : screenForDeviceName(w, forceToScreen); } // Returns topmost QWindowsWindow ancestor even if there are embedded windows in the chain. @@ -1593,7 +1737,7 @@ QWindowsWindowData void QWindowsWindow::setVisible(bool visible) { const QWindow *win = window(); - qCDebug(lcQpaWindows) << __FUNCTION__ << this << win << m_data.hwnd << visible; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << win << m_data.hwnd << visible; if (m_data.hwnd) { if (visible) { show_sys(); @@ -1773,7 +1917,7 @@ void QWindowsWindow::show_sys() const void QWindowsWindow::setParent(const QPlatformWindow *newParent) { - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << newParent; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << newParent; if (m_data.hwnd) setParent_sys(newParent); @@ -1833,6 +1977,101 @@ void QWindowsWindow::handleCompositionSettingsChanged() } } +qreal QWindowsWindow::dpiRelativeScale(const UINT dpi) const +{ + return QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) / + QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi); +} + +void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result) +{ + // We want to keep QWindow's device independent size constant across the + // DPI change. To accomplish this, scale QPlatformWindow's native size + // by the change of DPI (e.g. 120 -> 144 = 1.2), also taking any scale + // factor rounding into account. The win32 window size includes the margins; + // add the margins for the new DPI to the window size. + const UINT dpi = UINT(wParam); + const qreal scale = dpiRelativeScale(dpi); + const QMargins margins = fullFrameMargins(); + 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 * scale) + customMargins()); + SIZE *size = reinterpret_cast<SIZE *>(lParam); + size->cx = windowSize.width(); + size->cy = windowSize.height(); + *result = true; // Inform Windows that we've set a size +} + +void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + const UINT dpi = HIWORD(wParam); + const qreal scale = dpiRelativeScale(dpi); + setSavedDpi(dpi); + + QWindowsThemeCache::clearThemeCache(hwnd); + + // Send screen change first, so that the new screen is set during any following resize + checkForScreenChanged(QWindowsWindow::FromDpiChange); + + if (!IsZoomed(hwnd)) + m_data.restoreGeometry.setSize(m_data.restoreGeometry.size() * scale); + + // We get WM_DPICHANGED in one of two situations: + // + // 1. The DPI change is a "spontaneous" DPI change as a result of e.g. + // the user dragging the window to a new screen. In this case Windows + // first sends WM_GETDPISCALEDSIZE, where we set the new window size, + // followed by this event where we apply the suggested window geometry + // to the native window. This will make sure the window tracks the mouse + // cursor during screen change, and also that the window size is scaled + // according to the DPI change. + // + // 2. The DPI change is a result of a setGeometry() call. In this case + // Qt has already scaled the window size for the new DPI. Further, Windows + // does not call WM_GETDPISCALEDSIZE, and also applies its own scaling + // to the already scaled window size. Since there is no need to set the + // window geometry again, and the provided geometry is incorrect, we omit + // making the SetWindowPos() call. + if (!m_inSetgeometry) { + updateFullFrameMargins(); + const auto prcNewWindow = reinterpret_cast<RECT *>(lParam); + SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); + // If the window does not have a frame, WM_MOVE and WM_SIZE won't be + // called which prevents the content from being scaled appropriately + // after a DPI change. + if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd)) + handleGeometryChange(); + } + + // Re-apply mask now that we have a new DPI, which have resulted in + // a new scale factor. + setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window())); +} + +void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd) +{ + const UINT dpi = GetDpiForWindow(hwnd); + const qreal scale = dpiRelativeScale(dpi); + setSavedDpi(dpi); + + checkForScreenChanged(QWindowsWindow::FromDpiChange); + + // Child windows do not get WM_GETDPISCALEDSIZE messages to inform + // Windows about the new size, so we need to manually scale them. + QRect currentGeometry = geometry(); + QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale); + setGeometry(scaledGeometry); +} + static QRect normalFrameGeometry(HWND hwnd) { WINDOWPLACEMENT wp; @@ -1851,7 +2090,7 @@ QRect QWindowsWindow::normalGeometry() const m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen); const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd); const QMargins margins = fakeFullScreen - ? QWindowsGeometryHint::frame(handle(), m_savedStyle, 0) + ? QWindowsGeometryHint::frame(window(), handle(), m_savedStyle, 0) : fullFrameMargins(); return frame.isValid() ? frame.marginsRemoved(margins) : frame; } @@ -1903,6 +2142,8 @@ static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow, void QWindowsWindow::setGeometry(const QRect &rectIn) { + QBoolBlocker b(m_inSetgeometry); + QRect rect = rectIn; // This means it is a call from QWindow::setFramePosition() and // the coordinates include the frame (size is still the contents rectangle). @@ -1910,8 +2151,12 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) const QMargins margins = frameMargins(); rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top())); } + if (m_windowState & Qt::WindowMinimized) m_data.geometry = rect; // Otherwise set by handleGeometryChange() triggered by event. + else + setWindowState(Qt::WindowNoState);// Update window state to WindowNoState unless minimized + if (m_data.hwnd) { // A ResizeEvent with resulting geometry will be sent. If we cannot // achieve that size (for example, window title minimal constraint), @@ -1922,7 +2167,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 { @@ -1937,8 +2182,41 @@ void QWindowsWindow::handleMoved() handleGeometryChange(); } -void QWindowsWindow::handleResized(int wParam) +void QWindowsWindow::handleResized(int wParam, LPARAM lParam) { + /* Prevents borderless windows from covering the taskbar when maximized. */ + if ((m_data.flags.testFlag(Qt::FramelessWindowHint) + || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint))) + && IsZoomed(m_data.hwnd)) { + const int resizedWidth = LOWORD(lParam); + const int resizedHeight = HIWORD(lParam); + + const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO monitorInfo = {}; + monitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfoW(monitor, &monitorInfo); + + int correctLeft = monitorInfo.rcMonitor.left; + int correctTop = monitorInfo.rcMonitor.top; + int correctWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left; + int correctHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + + if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) { + const int borderWidth = invisibleMargins(m_data.hwnd).left(); + correctLeft -= borderWidth; + correctTop -= borderWidth; + correctWidth += borderWidth * 2; + correctHeight += borderWidth * 2; + } + + if (resizedWidth != correctWidth || resizedHeight != correctHeight) { + qCDebug(lcQpaWindow) << __FUNCTION__ << "correcting: " << resizedWidth << "x" + << resizedHeight << " -> " << correctWidth << "x" << correctHeight; + SetWindowPos(m_data.hwnd, nullptr, correctLeft, correctTop, correctWidth, correctHeight, + SWP_NOZORDER | SWP_NOACTIVATE); + } + } + switch (wParam) { case SIZE_MAXHIDE: // Some other window affected. case SIZE_MAXSHOW: @@ -1974,21 +2252,23 @@ static inline bool equalDpi(const QDpi &d1, const QDpi &d2) void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode) { - if (parent() || QWindowsScreenManager::isSingleScreen()) + if ((parent() && !parent()->isForeignWindow()) || QWindowsScreenManager::isSingleScreen()) return; QPlatformScreen *currentScreen = screen(); + auto topLevel = isTopLevel_sys() ? m_data.hwnd : GetAncestor(m_data.hwnd, GA_ROOT); const QWindowsScreen *newScreen = - QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd); + QWindowsContext::instance()->screenManager().screenForHwnd(topLevel); + if (newScreen == nullptr || newScreen == currentScreen) return; // For screens with different DPI: postpone until WM_DPICHANGE // Check on currentScreen as it can be 0 when resuming a session (QTBUG-80436). - if (mode == FromGeometryChange && currentScreen != nullptr - && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) { + const bool changingDpi = !equalDpi(QDpi(savedDpi(), savedDpi()), newScreen->logicalDpi()); + if (mode == FromGeometryChange && currentScreen != nullptr && changingDpi) return; - } - qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ + + qCDebug(lcQpaWindow).noquote().nospace() << __FUNCTION__ << ' ' << window() << " \"" << (currentScreen ? currentScreen->name() : QString()) << "\"->\"" << newScreen->name() << '"'; updateFullFrameMargins(); @@ -1999,6 +2279,7 @@ void QWindowsWindow::handleGeometryChange() { const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); + updateFullFrameMargins(); QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE // which we no longer support in Qt 6) do not receive expose @@ -2016,6 +2297,9 @@ void QWindowsWindow::handleGeometryChange() if (testFlag(SynchronousGeometryChangeEvent)) QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); + if (!testFlag(ResizeMoveActive)) + updateRestoreGeometry(); + if (!wasSync) clearFlag(SynchronousGeometryChangeEvent); qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry; @@ -2026,7 +2310,7 @@ void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const const QMargins margins = fullFrameMargins(); const QRect frameGeometry = rect + margins; - qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << window() + qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << window() << "\n from " << geometry_sys() << " frame: " << margins << " to " <<rect << " new frame: " << frameGeometry; @@ -2057,7 +2341,7 @@ void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const result = MoveWindow(hwnd, x, frameGeometry.y(), frameGeometry.width(), frameGeometry.height(), true); } - qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << window() + qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << window() << "\n resulting " << result << geometry_sys(); } @@ -2079,7 +2363,7 @@ HDC QWindowsWindow::getDC() } /*! - Relases the HDC for the window or does nothing in + Releases the HDC for the window or does nothing in case it was obtained from WinAPI BeginPaint within a WM_PAINT event. \sa getDC() @@ -2104,12 +2388,14 @@ static inline bool isSoftwareGl() } bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, - WPARAM, LPARAM) + WPARAM, LPARAM, LRESULT *result) { - if (message == WM_ERASEBKGND) // Backing store - ignored. + if (message == WM_ERASEBKGND) { // Backing store - ignored. + *result = 1; return true; + } // QTBUG-75455: Suppress WM_PAINT sent to invisible windows when setting WS_EX_LAYERED - if (!window()->isVisible() && (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0) + if (!window()->isVisible() && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0) return false; // Ignore invalid update bounding rectangles if (!GetUpdateRect(m_data.hwnd, 0, FALSE)) @@ -2141,7 +2427,7 @@ void QWindowsWindow::setWindowTitle(const QString &title) void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags) { - qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window() << "\n from: " + qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << this << window() << "\n from: " << m_data.flags << "\n to: " << flags; const QRect oldGeometry = geometry(); if (m_data.flags != flags) { @@ -2159,7 +2445,7 @@ void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags) if (oldGeometry != newGeometry) handleGeometryChange(); - qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << "\n returns: " + qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << "\n returns: " << m_data.flags << " geometry " << oldGeometry << "->" << newGeometry; } @@ -2174,13 +2460,14 @@ 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; } void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state) { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << "\n from " << m_windowState << " to " << state; m_windowState = state; QWindowSystemInterface::handleWindowStateChanged(window(), state); @@ -2188,6 +2475,14 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state) handleHidden(); QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); // Tell QQuickWindow to stop rendering now. } else { + if (state & Qt::WindowMaximized) { + WINDOWPLACEMENT windowPlacement{}; + windowPlacement.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(m_data.hwnd, &windowPlacement); + const RECT geometry = RECTfromQRect(m_data.restoreGeometry); + windowPlacement.rcNormalPosition = geometry; + SetWindowPlacement(m_data.hwnd, &windowPlacement); + } // QTBUG-17548: We send expose events when receiving WM_Paint, but for // layered windows and transient children, we won't receive any WM_Paint. QWindow *w = window(); @@ -2211,6 +2506,11 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state) } } +void QWindowsWindow::updateRestoreGeometry() +{ + m_data.restoreGeometry = normalFrameGeometry(m_data.hwnd); +} + void QWindowsWindow::setWindowState(Qt::WindowStates state) { if (m_data.hwnd) { @@ -2247,7 +2547,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState) const Qt::WindowStates oldState = m_windowState; if (oldState == newState) return; - qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window() + qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << this << window() << " from " << oldState << " to " << newState; const bool visible = isVisible(); @@ -2255,11 +2555,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState) if (stateChange & Qt::WindowFullScreen) { if (newState & Qt::WindowFullScreen) { -#ifndef Q_FLATTEN_EXPOSE UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; -#else - UINT newStyle = WS_POPUP; -#endif // Save geometry and style to be restored when fullscreen // is turned off again, since on Windows, it is not a real // Window state but emulated by changing geometry and style. @@ -2282,26 +2578,26 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState) if (testFlag(HasBorderInFullScreen)) newStyle |= WS_BORDER; setStyle(newStyle); - // Use geometry of QWindow::screen() within creation or the virtual screen the - // window is in (QTBUG-31166, QTBUG-30724). - const QScreen *screen = window()->screen(); - if (!screen) - screen = QGuiApplication::primaryScreen(); - const QRect r = screen ? QHighDpi::toNativePixels(screen->geometry(), window()) : m_savedFrameGeometry; - + const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO monitorInfo = {}; + monitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfoW(monitor, &monitorInfo); + const QRect screenGeometry(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, + monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top); if (newState & Qt::WindowMinimized) { - setMinimizedGeometry(m_data.hwnd, r); + setMinimizedGeometry(m_data.hwnd, screenGeometry); if (stateChange & Qt::WindowMaximized) setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized); } else { const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE; const bool wasSync = testFlag(SynchronousGeometryChangeEvent); setFlag(SynchronousGeometryChangeEvent); - SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf); + SetWindowPos(m_data.hwnd, HWND_TOP, screenGeometry.left(), screenGeometry.top(), screenGeometry.width(), screenGeometry.height(), swpf); if (!wasSync) clearFlag(SynchronousGeometryChangeEvent); clearFlag(MaximizeToFullScreen); - QWindowSystemInterface::handleGeometryChange(window(), r); + QWindowSystemInterface::handleGeometryChange(window(), screenGeometry); QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); } } else { @@ -2373,12 +2669,12 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState) setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized); } } - qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << this << window() << newState; + qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << this << window() << newState; } void QWindowsWindow::setStyle(unsigned s) const { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << debugWinStyle(s); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << debugWinStyle(s); setFlag(WithinSetStyle); SetWindowLongPtr(m_data.hwnd, GWL_STYLE, s); clearFlag(WithinSetStyle); @@ -2386,14 +2682,16 @@ void QWindowsWindow::setStyle(unsigned s) const void QWindowsWindow::setExStyle(unsigned s) const { - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << this << ' ' << window() - << " 0x" << QByteArray::number(s, 16); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << debugWinExStyle(s); SetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE, s); } bool QWindowsWindow::windowEvent(QEvent *event) { switch (event->type()) { + case QEvent::ApplicationPaletteChange: + setDarkBorder(QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark); + break; case QEvent::WindowBlocked: // Blocked by another modal window. setEnabled(false); setFlag(BlockedByModal); @@ -2413,12 +2711,24 @@ bool QWindowsWindow::windowEvent(QEvent *event) void QWindowsWindow::propagateSizeHints() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window(); } bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins) { auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam); + const QRect suggestedFrameGeometry(windowPos->x, windowPos->y, + windowPos->cx, windowPos->cy); + const QRect suggestedGeometry = suggestedFrameGeometry - margins; + + // Tell Windows to discard the entire contents of the client area, as re-using + // parts of the client area would lead to jitter during resize. + // Check the suggestedGeometry against the current one to only discard during + // resize, and not a plain move. We also look for SWP_NOSIZE since that, too, + // implies an identical size, and comparing QRects wouldn't work with null cx/cy + if (!(windowPos->flags & SWP_NOSIZE) && suggestedGeometry.size() != qWindow->geometry().size()) + windowPos->flags |= SWP_NOCOPYBITS; + if ((windowPos->flags & SWP_NOZORDER) == 0) { if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) { QWindow *parentWindow = qWindow->parent(); @@ -2431,11 +2741,8 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow * } if (!qWindow->isTopLevel()) // Implement hasHeightForWidth(). return false; - if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE))) + if (windowPos->flags & SWP_NOSIZE) return false; - const QRect suggestedFrameGeometry(windowPos->x, windowPos->y, - windowPos->cx, windowPos->cy); - const QRect suggestedGeometry = suggestedFrameGeometry - margins; const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry); if (!correctedGeometryF.isValid()) return false; @@ -2457,8 +2764,10 @@ bool QWindowsWindow::handleGeometryChanging(MSG *message) const void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins) { + if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd)) + return; if (m_data.fullFrameMargins != newMargins) { - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins; m_data.fullFrameMargins = newMargins; } } @@ -2474,30 +2783,66 @@ void QWindowsWindow::updateFullFrameMargins() void QWindowsWindow::calculateFullFrameMargins() { + if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd)) + return; + + // QTBUG-113736: systemMargins depends on AdjustWindowRectExForDpi. This doesn't take into + // account possible external modifications to the titlebar, as with ExtendsContentIntoTitleBar() + // from the Windows App SDK. We can fix this by comparing the WindowRect (which includes the + // frame) to the ClientRect. If a 'typical' frame is detected, i.e. only the titlebar has been + // modified, we can safely adjust the frame by deducting the bottom margin to the total Y + // difference between the two rects, to get the actual size of the titlebar and prevent + // unwanted client area slicing. + + RECT windowRect{}; + RECT clientRect{}; + GetWindowRect(handle(), &windowRect); + GetClientRect(handle(), &clientRect); + + // QTBUG-117704 It is also possible that the user has manually removed the frame (for example + // by handling WM_NCCALCSIZE). If that is the case, i.e., the client area and the window area + // have identical sizes, we don't want to override the user-defined margins. + + if (qrectFromRECT(windowRect).size() == qrectFromRECT(clientRect).size()) + return; + // Normally obtained from WM_NCCALCSIZE. This calculation only works // when no native menu is present. const auto systemMargins = testFlag(DisableNonClientScaling) - ? QWindowsGeometryHint::frameOnPrimaryScreen(m_data.hwnd) + ? QWindowsGeometryHint::frameOnPrimaryScreen(window(), m_data.hwnd) : frameMargins_sys(); - setFullFrameMargins(systemMargins + m_data.customMargins); + const QMargins actualMargins = systemMargins + customMargins(); + + const int yDiff = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top); + const bool typicalFrame = (actualMargins.left() == actualMargins.right()) + && (actualMargins.right() == actualMargins.bottom()); + + const QMargins adjustedMargins = typicalFrame ? + QMargins(actualMargins.left(), (yDiff - actualMargins.bottom()), + actualMargins.right(), actualMargins.bottom()) + : actualMargins; + + setFullFrameMargins(adjustedMargins); } QMargins QWindowsWindow::frameMargins() const { QMargins result = fullFrameMargins(); if (isTopLevel() && m_data.hasFrame) - result -= invisibleMargins(geometry().topLeft()); + result -= invisibleMargins(m_data.hwnd); return result; } QMargins QWindowsWindow::fullFrameMargins() const { + if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd)) + return {}; return m_data.fullFrameMargins; } void QWindowsWindow::setOpacity(qreal level) { - qCDebug(lcQpaWindows) << __FUNCTION__ << level; + qCDebug(lcQpaWindow) << __FUNCTION__ << level; if (!qFuzzyCompare(m_opacity, level)) { m_opacity = level; if (m_data.hwnd) @@ -2557,41 +2902,76 @@ void QWindowsWindow::setMask(const QRegion ®ion) void QWindowsWindow::requestActivateWindow() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); - // 'Active' state handling is based in focus since it needs to work for - // child windows as well. - if (m_data.hwnd) { - const DWORD currentThread = GetCurrentThreadId(); - bool attached = false; - DWORD foregroundThread = 0; - - // QTBUG-14062, QTBUG-37435: Windows normally only flashes the taskbar entry - // when activating windows of inactive applications. Attach to the input of the - // currently active window while setting the foreground window to always activate - // the window when desired. - const auto activationBehavior = QWindowsIntegration::instance()->windowActivationBehavior(); - if (QGuiApplication::applicationState() != Qt::ApplicationActive - && activationBehavior == QWindowsApplication::AlwaysActivateWindow) { - if (const HWND foregroundWindow = GetForegroundWindow()) { - foregroundThread = GetWindowThreadProcessId(foregroundWindow, nullptr); - if (foregroundThread && foregroundThread != currentThread) - attached = AttachThreadInput(foregroundThread, currentThread, TRUE) == TRUE; - if (attached) { - if (!window()->flags().testFlag(Qt::WindowStaysOnBottomHint) - && !window()->flags().testFlag(Qt::WindowStaysOnTopHint) - && window()->type() != Qt::ToolTip) { - const UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER; - SetWindowPos(m_data.hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags); - SetWindowPos(m_data.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags); - } - } - } - } + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window(); + + if (!m_data.hwnd) + return; + + const auto activationBehavior = QWindowsIntegration::instance()->windowActivationBehavior(); + if (QGuiApplication::applicationState() == Qt::ApplicationActive + || activationBehavior != QWindowsApplication::AlwaysActivateWindow) { SetForegroundWindow(m_data.hwnd); SetFocus(m_data.hwnd); - if (attached) - AttachThreadInput(foregroundThread, currentThread, FALSE); + return; + } + + // Force activate this window. The following code will bring the window to the + // foreground and activate it. If the window is hidden, it will show up. If + // the window is minimized, it will restore to the previous position. + + // But first we need some sanity checks. + if (m_data.flags & Qt::WindowStaysOnBottomHint) { + qCWarning(lcQpaWindow) << + "Windows with Qt::WindowStaysOnBottomHint can't be brought to the foreground."; + return; } + if (m_data.flags & Qt::WindowStaysOnTopHint) { + qCWarning(lcQpaWindow) << + "Windows with Qt::WindowStaysOnTopHint will always be on the foreground."; + return; + } + if (window()->type() == Qt::ToolTip) { + qCWarning(lcQpaWindow) << "ToolTip windows should not be activated."; + return; + } + + // We need to show the window first, otherwise we won't be able to bring it to front. + if (!IsWindowVisible(m_data.hwnd)) + ShowWindow(m_data.hwnd, SW_SHOW); + + if (IsIconic(m_data.hwnd)) { + ShowWindow(m_data.hwnd, SW_RESTORE); + // When the window is restored, it will always become the foreground window. + // So return early here, we don't need the following code to bring it to front. + return; + } + + // OK, our window is not minimized, so now we will try to bring it to front manually. + const HWND oldForegroundWindow = GetForegroundWindow(); + if (!oldForegroundWindow) // It may be NULL, according to MS docs. + return; + + // First try to send a message to the current foreground window to check whether + // it is currently hanging or not. + if (SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0, + SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, nullptr) == 0) { + qCWarning(lcQpaWindow) << "The foreground window hangs, can't activate current window."; + return; + } + + const DWORD windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr); + const DWORD currentThreadId = GetCurrentThreadId(); + + AttachThreadInput(windowThreadProcessId, currentThreadId, TRUE); + const auto cleanup = qScopeGuard([windowThreadProcessId, currentThreadId](){ + AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE); + }); + + BringWindowToTop(m_data.hwnd); + + // Activate the window too. This will force us to the virtual desktop this + // window is on, if it's on another virtual desktop. + SetActiveWindow(m_data.hwnd); } bool QWindowsWindow::setKeyboardGrabEnabled(bool grab) @@ -2600,7 +2980,7 @@ bool QWindowsWindow::setKeyboardGrabEnabled(bool grab) qWarning("%s: No handle", __FUNCTION__); return false; } - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << grab; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << grab; QWindowsContext *context = QWindowsContext::instance(); if (grab) { @@ -2614,7 +2994,7 @@ bool QWindowsWindow::setKeyboardGrabEnabled(bool grab) bool QWindowsWindow::setMouseGrabEnabled(bool grab) { - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << grab; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << grab; if (!m_data.hwnd) { qWarning("%s: No handle", __FUNCTION__); return false; @@ -2686,49 +3066,10 @@ void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled) } } -static int getBorderWidth(const QPlatformScreen *screen) -{ - NONCLIENTMETRICS ncm; - QWindowsContext::nonClientMetricsForScreen(&ncm, screen); - return ncm.iBorderWidth + ncm.iPaddedBorderWidth + 2; -} - void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const { QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi); - - // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the - // taskbar when maximized - if ((testFlag(WithinMaximize) || window()->windowStates().testFlag(Qt::WindowMinimized)) - && (m_data.flags.testFlag(Qt::FramelessWindowHint) - || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint)))) { - const QScreen *screen = window()->screen(); - - // Documentation of MINMAXINFO states that it will only work for the primary screen - if (screen && screen == QGuiApplication::primaryScreen()) { - const QRect availableGeometry = QHighDpi::toNativePixels(screen->availableGeometry(), screen); - mmi->ptMaxSize.y = availableGeometry.height(); - - // Width, because you can have the taskbar on the sides too. - mmi->ptMaxSize.x = availableGeometry.width(); - - // If you have the taskbar on top, or on the left you don't want it at (0,0): - mmi->ptMaxPosition.x = availableGeometry.x(); - mmi->ptMaxPosition.y = availableGeometry.y(); - if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) { - const int borderWidth = getBorderWidth(screen->handle()); - mmi->ptMaxSize.x += borderWidth * 2; - mmi->ptMaxSize.y += borderWidth * 2; - mmi->ptMaxTrackSize = mmi->ptMaxSize; - mmi->ptMaxPosition.x -= borderWidth; - mmi->ptMaxPosition.y -= borderWidth; - } - } else if (!screen){ - qWarning("window()->screen() returned a null screen"); - } - } - - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << *mmi; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << *mmi; } bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *result) const @@ -2757,12 +3098,7 @@ bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *re return true; } if (localPos.y() < 0) { - // We want to return HTCAPTION/true only over the outer sizing frame, not the entire title bar, - // otherwise the title bar buttons (close, etc.) become unresponsive on Windows 7 (QTBUG-78262). - // However, neither frameMargins() nor GetSystemMetrics(SM_CYSIZEFRAME), etc., give the correct - // sizing frame height in all Windows versions/scales. This empirical constant seems to work, though. - const int sizingHeight = 9; - const int topResizeBarPos = sizingHeight - frameMargins().top(); + const int topResizeBarPos = invisibleMargins(m_data.hwnd).left() - frameMargins().top(); if (localPos.y() < topResizeBarPos) { *result = HTCAPTION; // Extend caption over top resize bar, let's user move the window. return true; @@ -2836,9 +3172,16 @@ void QWindowsWindow::applyCursor() void QWindowsWindow::setCursor(const CursorHandlePtr &c) { #ifndef QT_NO_CURSOR - if (c->handle() != m_cursor->handle()) { + bool changed = c->handle() != m_cursor->handle(); + // QTBUG-98856: Cursors can get out of sync after restoring override + // cursors on native windows. Force an update. + if (testFlag(RestoreOverrideCursor)) { + clearFlag(RestoreOverrideCursor); + changed = true; + } + if (changed) { const bool apply = applyNewCursor(window()); - qCDebug(lcQpaWindows) << window() << __FUNCTION__ + qCDebug(lcQpaWindow) << window() << __FUNCTION__ << c->handle() << " doApply=" << apply; m_cursor = c; if (apply) @@ -2943,17 +3286,6 @@ enum : WORD { DwmwaUseImmersiveDarkModeBefore20h1 = 19 }; -static bool queryDarkBorder(HWND hwnd) -{ - BOOL result = FALSE; - const bool ok = - SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result))) - || SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result))); - if (!ok) - qWarning("%s: Unable to retrieve dark window border setting.", __FUNCTION__); - return result == TRUE; -} - bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d) { const BOOL darkBorder = d ? TRUE : FALSE; @@ -2961,14 +3293,16 @@ bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d) SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &darkBorder, sizeof(darkBorder))) || SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &darkBorder, sizeof(darkBorder))); if (!ok) - qWarning("%s: Unable to set dark window border.", __FUNCTION__); + qCWarning(lcQpaWindow, "%s: Unable to set %s window border.", __FUNCTION__, d ? "dark" : "light"); return ok; } 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()); + + setDarkBorderToWindow(m_data.hwnd, d); } QWindowsMenuBar *QWindowsWindow::menuBar() const @@ -2981,6 +3315,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. @@ -2993,6 +3334,10 @@ void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb) void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) { + if (m_data.flags & Qt::FramelessWindowHint) { + qCWarning(lcQpaWindow) << "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; @@ -3001,7 +3346,7 @@ void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) const QPoint topLeft = currentFrameGeometry.topLeft(); QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins; newFrame.moveTo(topLeft); - qCDebug(lcQpaWindows) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins + qCDebug(lcQpaWindow) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins << currentFrameGeometry << "->" << newFrame; SetWindowPos(m_data.hwnd, nullptr, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE); } @@ -3027,7 +3372,7 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err) #elif defined(QT_NO_OPENGL) Q_UNUSED(err); Q_UNUSED(nativeConfig); - return 0; + return nullptr; #endif #ifndef QT_NO_OPENGL if (!m_surface) { @@ -3084,24 +3429,6 @@ void QWindowsWindow::registerTouchWindow() qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName())); } -void QWindowsWindow::aboutToMakeCurrent() -{ -#ifndef QT_NO_OPENGL - // For RasterGLSurface windows, that become OpenGL windows dynamically, it might be - // time to set up some GL specifics. This is particularly important for layered - // windows (WS_EX_LAYERED due to alpha > 0). - const bool isCompositing = qt_window_private(window())->compositing; - if (isCompositing != testFlag(Compositing)) { - if (isCompositing) - setFlag(Compositing); - else - clearFlag(Compositing); - - updateGLWindowSettings(window(), m_data.hwnd, m_data.flags, m_opacity); - } -#endif -} - void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border) { if (QPlatformWindow *handle = window->handle()) diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index ea170aeee1..024711e7f3 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSWINDOW_H #define QWINDOWSWINDOW_H @@ -62,10 +26,11 @@ class QDebug; struct QWindowsGeometryHint { - static QMargins frameOnPrimaryScreen(DWORD style, DWORD exStyle); - static QMargins frameOnPrimaryScreen(HWND hwnd); - static QMargins frame(DWORD style, DWORD exStyle, qreal dpi); - static QMargins frame(HWND hwnd, DWORD style, DWORD exStyle); + static QMargins frameOnPrimaryScreen(const QWindow *w, DWORD style, DWORD exStyle); + static QMargins frameOnPrimaryScreen(const QWindow *w, HWND hwnd); + static QMargins frame(const QWindow *w, DWORD style, DWORD exStyle, qreal dpi); + static QMargins frame(const QWindow *w, HWND hwnd, DWORD style, DWORD exStyle); + static QMargins frame(const QWindow *w, HWND hwnd); static QMargins frame(const QWindow *w, const QRect &geometry, DWORD style, DWORD exStyle); static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result); @@ -113,6 +78,7 @@ struct QWindowsWindowData { Qt::WindowFlags flags; QRect geometry; + QRect restoreGeometry; QMargins fullFrameMargins; // Do not use directly for windows, see FrameDirty. QMargins customMargins; // User-defined, additional frame for NCCALCSIZE HWND hwnd = nullptr; @@ -238,7 +204,8 @@ public: VulkanSurface = 0x400000, ResizeMoveActive = 0x800000, DisableNonClientScaling = 0x1000000, - Direct3DSurface = 0x2000000 + Direct3DSurface = 0x2000000, + RestoreOverrideCursor = 0x4000000 }; QWindowsWindow(QWindow *window, const QWindowsWindowData &data); @@ -252,6 +219,8 @@ public: void setGeometry(const QRect &rect) override; QRect geometry() const override { return m_data.geometry; } QRect normalGeometry() const override; + QRect restoreGeometry() const { return m_data.restoreGeometry; } + void updateRestoreGeometry(); void setVisible(bool visible) override; bool isVisible() const; @@ -306,18 +275,21 @@ 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; void setExStyle(unsigned s) const; - bool handleWmPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + bool handleWmPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); void handleMoved(); - void handleResized(int wParam); + void handleResized(int wParam, LPARAM lParam); void handleHidden(); void handleCompositionSettingsChanged(); + void handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result); + void handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam); + void handleDpiChangedAfterParent(HWND hwnd); static void displayChanged(); static void settingsChanged(); @@ -327,6 +299,7 @@ public: static inline void *userDataOf(HWND hwnd); static inline void setUserDataOf(HWND hwnd, void *ud); + static bool hasNoNativeFrame(HWND hwnd, Qt::WindowFlags flags); static bool setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity); bool isLayered() const; @@ -351,7 +324,6 @@ public: void *surface(void *nativeConfig, int *err); void invalidateSurface() override; - void aboutToMakeCurrent(); void setAlertState(bool enabled) override; bool isAlertState() const override { return testFlag(AlertState); } @@ -373,6 +345,7 @@ public: void setSavedDpi(int dpi) { m_savedDpi = dpi; } int savedDpi() const { return m_savedDpi; } + qreal dpiRelativeScale(const UINT dpi) const; private: inline void show_sys() const; @@ -416,6 +389,7 @@ private: VkSurfaceKHR m_vkSurface = VK_NULL_HANDLE; #endif static bool m_borderInFullScreenDefault; + static bool m_inSetgeometry; }; #ifndef QT_NO_DEBUG_STREAM diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp index 0903f5a618..1abb412ccd 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp @@ -1,46 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) #include "qwindowsuiaaccessibility.h" +#include "qwindowsuiautomation.h" #include "qwindowsuiamainprovider.h" #include "qwindowsuiautils.h" @@ -50,14 +15,15 @@ #include <QtGui/private/qguiapplication_p.h> #include <QtCore/qt_windows.h> #include <qpa/qplatformintegration.h> -#include <QtGui/private/qwindowsuiawrapper_p.h> #include <QtCore/private/qwinregistry_p.h> QT_BEGIN_NAMESPACE using namespace QWindowsUiAutomation; +using namespace Qt::Literals::StringLiterals; +bool QWindowsUiaAccessibility::m_accessibleActive = false; QWindowsUiaAccessibility::QWindowsUiaAccessibility() { @@ -72,6 +38,7 @@ bool QWindowsUiaAccessibility::handleWmGetObject(HWND hwnd, WPARAM wParam, LPARA { // Start handling accessibility internally QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true); + m_accessibleActive = true; // Ignoring all requests while starting up / shutting down if (QCoreApplication::startingUp() || QCoreApplication::closingDown()) @@ -80,7 +47,7 @@ bool QWindowsUiaAccessibility::handleWmGetObject(HWND hwnd, WPARAM wParam, LPARA if (QWindow *window = QWindowsContext::instance()->findWindow(hwnd)) { if (QAccessibleInterface *accessible = window->accessibleRoot()) { QWindowsUiaMainProvider *provider = QWindowsUiaMainProvider::providerForAccessible(accessible); - *lResult = QWindowsUiaWrapper::instance()->returnRawElementProvider(hwnd, wParam, lParam, provider); + *lResult = UiaReturnRawElementProvider(hwnd, wParam, lParam, provider); return true; } } @@ -112,8 +79,8 @@ static QString alertSound(const QObject *object) static QString soundFileName(const QString &soundName) { - const QString key = QStringLiteral("AppEvents\\Schemes\\Apps\\.Default\\") - + soundName + QStringLiteral("\\.Current"); + const QString key = "AppEvents\\Schemes\\Apps\\.Default\\"_L1 + + soundName + "\\.Current"_L1; return QWinRegistryKey(HKEY_CURRENT_USER, key).stringValue(L""); } @@ -131,6 +98,7 @@ void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event if (!event) return; + // Always handle system sound events switch (event->type()) { case QAccessible::PopupMenuStart: playSystemSound(QStringLiteral("MenuPopup")); @@ -145,16 +113,17 @@ void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event break; } - QAccessibleInterface *accessible = event->accessibleInterface(); - if (!isActive() || !accessible || !accessible->isValid()) + // Ignore events sent before the first UI Automation + // request or while QAccessible is being activated. + if (!m_accessibleActive) return; - // Ensures QWindowsUiaWrapper is properly initialized. - if (!QWindowsUiaWrapper::instance()->ready()) + QAccessibleInterface *accessible = event->accessibleInterface(); + if (!isActive() || !accessible || !accessible->isValid()) return; // No need to do anything when nobody is listening. - if (!QWindowsUiaWrapper::instance()->clientsAreListening()) + if (!UiaClientsAreListening()) return; switch (event->type()) { diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h index 48b4f9fa6a..2e8ee585da 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAACCESSIBILITY_H #define QWINDOWSUIAACCESSIBILITY_H @@ -43,6 +7,7 @@ #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) +#include <QtCore/qt_windows.h> #include "qwindowscontext.h" #include <qpa/qplatformaccessibility.h> @@ -56,6 +21,8 @@ public: virtual ~QWindowsUiaAccessibility(); static bool handleWmGetObject(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult); void notifyAccessibilityUpdate(QAccessibleEvent *event) override; +private: + static bool m_accessibleActive; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.cpp index 53c647512a..9abfcae247 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h index a14eb8ca65..032679ab10 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIABASEPROVIDER_H #define QWINDOWSUIABASEPROVIDER_H @@ -46,8 +10,8 @@ #include <QtGui/qaccessible.h> #include <QtCore/qpointer.h> -#include <qwindowscombase.h> -#include <QtGui/private/qwindowsuiawrapper_p.h> +#include "qwindowsuiautomation.h" +#include <QtCore/private/qcomobject_p.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.cpp index 6ac8de23fa..8eb9baa8ef 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) @@ -52,6 +16,14 @@ QT_BEGIN_NAMESPACE using namespace QWindowsUiAutomation; +static bool isExpanded(QAccessibleInterface *accessible) +{ + Q_ASSERT(accessible); + if (accessible->role() == QAccessible::MenuItem) + return accessible->childCount() > 0 && !accessible->child(0)->state().invisible; + else + return accessible->state().expandable && accessible->state().expanded; +} QWindowsUiaExpandCollapseProvider::QWindowsUiaExpandCollapseProvider(QAccessible::Id id) : QWindowsUiaBaseProvider(id) @@ -72,7 +44,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaExpandCollapseProvider::Expand() if (!actionInterface) return UIA_E_ELEMENTNOTAVAILABLE; - if (accessible->childCount() > 0 && accessible->child(0)->state().invisible) + if (!isExpanded(accessible)) actionInterface->doAction(QAccessibleActionInterface::showMenuAction()); return S_OK; @@ -90,7 +62,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaExpandCollapseProvider::Collapse() if (!actionInterface) return UIA_E_ELEMENTNOTAVAILABLE; - if (accessible->childCount() > 0 && !accessible->child(0)->state().invisible) + if (isExpanded(accessible)) actionInterface->doAction(QAccessibleActionInterface::showMenuAction()); return S_OK; @@ -108,9 +80,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaExpandCollapseProvider::get_ExpandCollapseS if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; - if (accessible->childCount() > 0) - *pRetVal = accessible->child(0)->state().invisible ? - ExpandCollapseState_Collapsed : ExpandCollapseState_Expanded; + *pRetVal = isExpanded(accessible) ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed; return S_OK; } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h index f5b4c2e78b..b384eb521c 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAEXPANDCOLLAPSEPROVIDER_H #define QWINDOWSUIAEXPANDCOLLAPSEPROVIDER_H @@ -49,7 +13,7 @@ QT_BEGIN_NAMESPACE // Implements the Expand/Collapse control pattern provider. Used for menu items with submenus. class QWindowsUiaExpandCollapseProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<IExpandCollapseProvider> + public QComObject<IExpandCollapseProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaExpandCollapseProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.cpp index 93d360c40b..a76128df58 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h index 3244122038..289a867869 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAGRIDITEMPROVIDER_H #define QWINDOWSUIAGRIDITEMPROVIDER_H @@ -49,7 +13,7 @@ QT_BEGIN_NAMESPACE // Implements the Grid Item control pattern provider. Used by items within a table/tree. class QWindowsUiaGridItemProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<IGridItemProvider> + public QComObject<IGridItemProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaGridItemProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.cpp index cce9d8143c..9f62ef5fbe 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h index 0e5f81108e..d33bbd0429 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAGRIDPROVIDER_H #define QWINDOWSUIAGRIDPROVIDER_H @@ -48,8 +12,7 @@ QT_BEGIN_NAMESPACE // Implements the Grid control pattern provider. Used by tables/trees. -class QWindowsUiaGridProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<IGridProvider> +class QWindowsUiaGridProvider : public QWindowsUiaBaseProvider, public QComObject<IGridProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaGridProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.cpp index d09770bc81..e22c46ac4c 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h index 7d646894a1..ec006c673e 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAINVOKEPROVIDER_H #define QWINDOWSUIAINVOKEPROVIDER_H @@ -48,8 +12,7 @@ QT_BEGIN_NAMESPACE // Implements the Invoke control pattern provider. -class QWindowsUiaInvokeProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<IInvokeProvider> +class QWindowsUiaInvokeProvider : public QWindowsUiaBaseProvider, public QComObject<IInvokeProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaInvokeProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index 171a3c268b..95ddbcced6 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) @@ -54,7 +18,6 @@ #include "qwindowsuiagriditemprovider.h" #include "qwindowsuiawindowprovider.h" #include "qwindowsuiaexpandcollapseprovider.h" -#include "qwindowscombase.h" #include "qwindowscontext.h" #include "qwindowsuiautils.h" #include "qwindowsuiaprovidercache.h" @@ -63,6 +26,7 @@ #include <QtGui/qaccessible.h> #include <QtGui/qguiapplication.h> #include <QtGui/qwindow.h> +#include <qpa/qplatforminputcontextfactory_p.h> #if !defined(Q_CC_BOR) && !defined (Q_CC_GNU) #include <comdef.h> @@ -74,10 +38,13 @@ QT_BEGIN_NAMESPACE using namespace QWindowsUiAutomation; +QMutex QWindowsUiaMainProvider::m_mutex; -// Returns a cached instance of the provider for a specific acessible interface. +// 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; @@ -94,9 +61,8 @@ QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessi return provider; } -QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a, int initialRefCount) - : QWindowsUiaBaseProvider(QAccessible::uniqueId(a)), - m_ref(initialRefCount) +QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a) + : QWindowsUiaBaseProvider(QAccessible::uniqueId(a)) { } @@ -107,20 +73,13 @@ 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()) { - int count = accessible->childCount(); - for (int i = 0; i < count; ++i) { - QAccessibleInterface *item = accessible->child(i); - if (item && item->isValid() && item->state().focused) { - accessible = item; - break; - } - } - } - if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { - QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId); + // 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)) + UiaRaiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId); } } @@ -137,7 +96,7 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve if (accessible->state().checked) toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On; setVariantI4(toggleState, &newVal); - QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ToggleToggleStatePropertyId, oldVal, newVal); + UiaRaiseAutomationPropertyChangedEvent(provider, UIA_ToggleToggleStatePropertyId, oldVal, newVal); } } } @@ -146,9 +105,13 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve // Notifies window opened/closed. if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { if (accessible->state().active) { - QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowOpenedEventId); + UiaRaiseAutomationEvent(provider, UIA_Window_WindowOpenedEventId); + if (QAccessibleInterface *focused = accessible->focusChild()) { + if (QWindowsUiaMainProvider *focusedProvider = providerForAccessible(focused)) + UiaRaiseAutomationEvent(focusedProvider, UIA_AutomationFocusChangedEventId); + } } else { - QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowClosedEventId); + UiaRaiseAutomationEvent(provider, UIA_Window_WindowClosedEventId); } } } @@ -177,27 +140,11 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve } if (event->value().typeId() == QMetaType::QString) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { - - // Tries to notify the change using UiaRaiseNotificationEvent(), which is only available on - // Windows 10 version 1709 or newer. Otherwise uses UiaRaiseAutomationPropertyChangedEvent(). - - BSTR displayString = bStrFromQString(event->value().toString()); - BSTR activityId = bStrFromQString(QString()); - - HRESULT hr = QWindowsUiaWrapper::instance()->raiseNotificationEvent(provider, NotificationKind_Other, - NotificationProcessing_ImportantMostRecent, - displayString, activityId); - - ::SysFreeString(displayString); - ::SysFreeString(activityId); - - if (hr == static_cast<HRESULT>(UIA_E_NOTSUPPORTED)) { - VARIANT oldVal, newVal; - clearVariant(&oldVal); - setVariantString(event->value().toString(), &newVal); - QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); - ::SysFreeString(newVal.bstrVal); - } + // Notifies changes in string values. + VARIANT oldVal, newVal; + clearVariant(&oldVal); + setVariantString(event->value().toString(), &newVal); + UiaRaiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); } } else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { @@ -205,7 +152,7 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve VARIANT oldVal, newVal; clearVariant(&oldVal); setVariantDouble(valueInterface->currentValue().toDouble(), &newVal); - QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_RangeValueValuePropertyId, oldVal, newVal); + UiaRaiseAutomationPropertyChangedEvent(provider, UIA_RangeValueValuePropertyId, oldVal, newVal); } } } @@ -214,12 +161,16 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *event) { if (QAccessibleInterface *accessible = event->accessibleInterface()) { - if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { - VARIANT oldVal, newVal; - clearVariant(&oldVal); - setVariantString(accessible->text(QAccessible::Name), &newVal); - QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_NamePropertyId, oldVal, newVal); - ::SysFreeString(newVal.bstrVal); + // Restrict notification to combo boxes, which need it for accessibility, + // in order to avoid slowdowns with unnecessary notifications. + if (accessible->role() == QAccessible::ComboBox) { + if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { + VARIANT oldVal, newVal; + clearVariant(&oldVal); + setVariantString(accessible->text(QAccessible::Name), &newVal); + UiaRaiseAutomationPropertyChangedEvent(provider, UIA_NamePropertyId, oldVal, newVal); + ::SysFreeString(newVal.bstrVal); + } } } } @@ -228,7 +179,7 @@ void QWindowsUiaMainProvider::notifySelectionChange(QAccessibleEvent *event) { if (QAccessibleInterface *accessible = event->accessibleInterface()) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { - QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_SelectionItem_ElementSelectedEventId); + UiaRaiseAutomationEvent(provider, UIA_SelectionItem_ElementSelectedEventId); } } } @@ -240,13 +191,13 @@ void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event) if (accessible->textInterface()) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { if (event->type() == QAccessible::TextSelectionChanged) { - QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId); + UiaRaiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId); } else if (event->type() == QAccessible::TextCaretMoved) { if (!accessible->state().readOnly) { - QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId); + UiaRaiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId); } } else { - QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextChangedEventId); + UiaRaiseAutomationEvent(provider, UIA_Text_TextChangedEventId); } } } @@ -255,31 +206,26 @@ void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event) HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface) { - if (!iface) - return E_INVALIDARG; - *iface = nullptr; - - QAccessibleInterface *accessible = accessibleInterface(); + HRESULT result = QComObject::QueryInterface(iid, iface); - const bool result = qWindowsComQueryUnknownInterfaceMulti<IRawElementProviderSimple>(this, iid, iface) - || qWindowsComQueryInterface<IRawElementProviderSimple>(this, iid, iface) - || qWindowsComQueryInterface<IRawElementProviderFragment>(this, iid, iface) - || (accessible && hwndForAccessible(accessible) && qWindowsComQueryInterface<IRawElementProviderFragmentRoot>(this, iid, iface)); - return result ? S_OK : E_NOINTERFACE; -} + if (SUCCEEDED(result) && iid == __uuidof(IRawElementProviderFragmentRoot)) { + QAccessibleInterface *accessible = accessibleInterface(); + if (accessible && hwndForAccessible(accessible)) { + result = S_OK; + } else { + result = E_NOINTERFACE; + iface = nullptr; + } + } -ULONG QWindowsUiaMainProvider::AddRef() -{ - return ++m_ref; + return result; } ULONG STDMETHODCALLTYPE QWindowsUiaMainProvider::Release() { - if (!--m_ref) { - delete this; - return 0; - } - return m_ref; + QMutexLocker locker(&m_mutex); + + return QComObject::Release(); } HRESULT QWindowsUiaMainProvider::get_ProviderOptions(ProviderOptions *pRetVal) @@ -334,15 +280,20 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow *pRetVal = new QWindowsUiaToggleProvider(id()); break; case UIA_SelectionPatternId: - // Lists of items. - if (accessible->role() == QAccessible::List) { + case UIA_SelectionPattern2Id: + // Selections via QAccessibleSelectionInterface or lists of items. + if (accessible->selectionInterface() + || accessible->role() == QAccessible::List + || accessible->role() == QAccessible::PageTabList) { *pRetVal = new QWindowsUiaSelectionProvider(id()); } break; case UIA_SelectionItemPatternId: - // Items within a list and radio buttons. - if ((accessible->role() == QAccessible::RadioButton) - || (accessible->role() == QAccessible::ListItem)) { + // Parent supports selection interface or items within a list and radio buttons. + if ((accessible->parent() && accessible->parent()->selectionInterface()) + || (accessible->role() == QAccessible::RadioButton) + || (accessible->role() == QAccessible::ListItem) + || (accessible->role() == QAccessible::PageTab)) { *pRetVal = new QWindowsUiaSelectionItemProvider(id()); } break; @@ -382,9 +333,11 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow break; case UIA_ExpandCollapsePatternId: // Menu items with submenus. - if (accessible->role() == QAccessible::MenuItem + if ((accessible->role() == QAccessible::MenuItem && accessible->childCount() > 0 - && accessible->child(0)->role() == QAccessible::PopupMenu) { + && accessible->child(0)->role() == QAccessible::PopupMenu) + || accessible->role() == QAccessible::ComboBox + || (accessible->role() == QAccessible::TreeItem && accessible->state().expandable)) { *pRetVal = new QWindowsUiaExpandCollapseProvider(id()); } break; @@ -395,6 +348,28 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow return S_OK; } +void QWindowsUiaMainProvider::fillVariantArrayForRelation(QAccessibleInterface* accessible, + QAccessible::Relation relation, VARIANT *pRetVal) +{ + Q_ASSERT(accessible); + + typedef QPair<QAccessibleInterface*, QAccessible::Relation> RelationPair; + const QList<RelationPair> relationInterfaces = accessible->relations(relation); + if (relationInterfaces.empty()) + return; + + SAFEARRAY *elements = SafeArrayCreateVector(VT_UNKNOWN, 0, relationInterfaces.size()); + for (LONG i = 0; i < relationInterfaces.size(); ++i) { + if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(relationInterfaces.at(i).first)) { + SafeArrayPutElement(elements, &i, static_cast<IRawElementProviderSimple*>(childProvider)); + childProvider->Release(); + } + } + + pRetVal->vt = VT_UNKNOWN | VT_ARRAY; + pRetVal->parray = elements; +} + HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal) { qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp; @@ -425,10 +400,19 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR case UIA_ClassNamePropertyId: // Class name. if (QObject *o = accessible->object()) { - QString className = QLatin1String(o->metaObject()->className()); + QString className = QLatin1StringView(o->metaObject()->className()); setVariantString(className, pRetVal); } break; + case UIA_DescribedByPropertyId: + fillVariantArrayForRelation(accessible, QAccessible::DescriptionFor, pRetVal); + break; + case UIA_FlowsFromPropertyId: + fillVariantArrayForRelation(accessible, QAccessible::FlowsTo, pRetVal); + break; + case UIA_FlowsToPropertyId: + fillVariantArrayForRelation(accessible, QAccessible::FlowsFrom, pRetVal); + break; case UIA_FrameworkIdPropertyId: setVariantString(QStringLiteral("Qt"), pRetVal); break; @@ -440,9 +424,9 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR // Control type converted from role. auto controlType = roleToControlTypeId(accessible->role()); - // The native OSK should be disbled if the Qt OSK is in use, + // The native OSK should be disabled if the Qt OSK is in use, // or if disabled via application attribute. - static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE"); + static bool imModuleEmpty = QPlatformInputContextFactory::requested().isEmpty(); bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard); // If we want to disable the native OSK auto-showing @@ -545,7 +529,7 @@ HRESULT QWindowsUiaMainProvider::get_HostRawElementProvider(IRawElementProviderS // Returns a host provider only for controls associated with a native window handle. Others should return NULL. if (QAccessibleInterface *accessible = accessibleInterface()) { if (HWND hwnd = hwndForAccessible(accessible)) { - return QWindowsUiaWrapper::instance()->hostProviderFromHwnd(hwnd, pRetVal); + return UiaHostProviderFromHwnd(hwnd, pRetVal); } } return S_OK; @@ -735,26 +719,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 f7320388f7..99db0ed318 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAMAINPROVIDER_H #define QWINDOWSUIAMAINPROVIDER_H @@ -47,6 +11,7 @@ #include <QtCore/qpointer.h> #include <QtCore/qsharedpointer.h> +#include <QtCore/qmutex.h> #include <QtCore/qt_windows.h> #include <QtGui/qaccessible.h> @@ -55,15 +20,13 @@ QT_BEGIN_NAMESPACE // The main UI Automation class. class QWindowsUiaMainProvider : public QWindowsUiaBaseProvider, - public IRawElementProviderSimple, - public IRawElementProviderFragment, - public IRawElementProviderFragmentRoot + public QComObject<IRawElementProviderSimple, IRawElementProviderFragment, IRawElementProviderFragmentRoot> { Q_OBJECT Q_DISABLE_COPY_MOVE(QWindowsUiaMainProvider) public: static QWindowsUiaMainProvider *providerForAccessible(QAccessibleInterface *accessible); - explicit QWindowsUiaMainProvider(QAccessibleInterface *a, int initialRefCount = 1); + explicit QWindowsUiaMainProvider(QAccessibleInterface *a); virtual ~QWindowsUiaMainProvider(); static void notifyFocusChange(QAccessibleEvent *event); static void notifyStateChange(QAccessibleStateChangeEvent *event); @@ -74,7 +37,6 @@ public: // IUnknown HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override; - ULONG STDMETHODCALLTYPE AddRef() override; ULONG STDMETHODCALLTYPE Release() override; // IRawElementProviderSimple methods @@ -97,7 +59,8 @@ public: private: QString automationIdForAccessible(const QAccessibleInterface *accessible); - ULONG m_ref; + static void fillVariantArrayForRelation(QAccessibleInterface *accessible, QAccessible::Relation relation, VARIANT *pRetVal); + 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 c55e827a46..ba2a88bb4e 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) @@ -75,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); } } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.h index f66dc2c170..08f315b4e6 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAPROVIDERCACHE_H #define QWINDOWSUIAPROVIDERCACHE_H diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.cpp index 7c1827387a..574bb7d2d6 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h index fc82d3a2cc..ffb5ae155b 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIARANGEVALUEPROVIDER_H #define QWINDOWSUIARANGEVALUEPROVIDER_H @@ -49,7 +13,7 @@ QT_BEGIN_NAMESPACE // Implements the Range Value control pattern provider. class QWindowsUiaRangeValueProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<IRangeValueProvider> + public QComObject<IRangeValueProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaRangeValueProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp index a93a05c7bc..95bc2f7570 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) @@ -72,12 +36,20 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::Select() if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; + if (QAccessibleInterface *parent = accessible->parent()) { + if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) { + selectionInterface->clear(); + bool ok = selectionInterface->select(accessible); + return ok ? S_OK : S_FALSE; + } + } + QAccessibleActionInterface *actionInterface = accessible->actionInterface(); if (!actionInterface) return UIA_E_ELEMENTNOTAVAILABLE; - if (accessible->role() == QAccessible::RadioButton) { - // For radio buttons we just invoke the selection action; others are automatically deselected. + if (accessible->role() == QAccessible::RadioButton || accessible->role() == QAccessible::PageTab) { + // For radio buttons/tabs we just invoke the selection action; others are automatically deselected. actionInterface->doAction(QAccessibleActionInterface::pressAction()); } else { // Toggle list item if not already selected. It must be done first to support all selection modes. @@ -109,12 +81,19 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::AddToSelection() if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; + if (QAccessibleInterface *parent = accessible->parent()) { + if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) { + bool ok = selectionInterface->select(accessible); + return ok ? S_OK : S_FALSE; + } + } + QAccessibleActionInterface *actionInterface = accessible->actionInterface(); if (!actionInterface) return UIA_E_ELEMENTNOTAVAILABLE; - if (accessible->role() == QAccessible::RadioButton) { - // For radio buttons we invoke the selection action. + if (accessible->role() == QAccessible::RadioButton || accessible->role() == QAccessible::PageTab) { + // For radio buttons and tabs we invoke the selection action. actionInterface->doAction(QAccessibleActionInterface::pressAction()); } else { // Toggle list item if not already selected. @@ -134,11 +113,18 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::RemoveFromSelection( if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; + if (QAccessibleInterface *parent = accessible->parent()) { + if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) { + bool ok = selectionInterface->unselect(accessible); + return ok ? S_OK : S_FALSE; + } + } + QAccessibleActionInterface *actionInterface = accessible->actionInterface(); if (!actionInterface) return UIA_E_ELEMENTNOTAVAILABLE; - if (accessible->role() != QAccessible::RadioButton) { + if (accessible->role() != QAccessible::RadioButton && accessible->role() != QAccessible::PageTab) { if (accessible->state().selected) { actionInterface->doAction(QAccessibleActionInterface::toggleAction()); } @@ -160,8 +146,18 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_IsSelected(BOOL if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; + if (QAccessibleInterface *parent = accessible->parent()) { + if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) { + bool selected = selectionInterface->isSelected(accessible); + *pRetVal = selected ? TRUE : FALSE; + return S_OK; + } + } + if (accessible->role() == QAccessible::RadioButton) *pRetVal = accessible->state().checked; + else if (accessible->role() == QAccessible::PageTab) + *pRetVal = accessible->state().focused; else *pRetVal = accessible->state().selected; return S_OK; @@ -180,16 +176,21 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_SelectionContain if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; + QAccessibleInterface *parent = accessible->parent(); + if (parent && parent->selectionInterface()) { + *pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent); + return S_OK; + } + QAccessibleActionInterface *actionInterface = accessible->actionInterface(); if (!actionInterface) return UIA_E_ELEMENTNOTAVAILABLE; // Radio buttons do not require a container. - if (accessible->role() == QAccessible::ListItem) { - if (QAccessibleInterface *parent = accessible->parent()) { - if (parent->role() == QAccessible::List) { - *pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent); - } + if (parent) { + if ((accessible->role() == QAccessible::ListItem && parent->role() == QAccessible::List) + || (accessible->role() == QAccessible::PageTab && parent->role() == QAccessible::PageTabList)) { + *pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent); } } return S_OK; diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h index 4b59c4e40f..ee34fd9edd 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIASELECTIONITEMPROVIDER_H #define QWINDOWSUIASELECTIONITEMPROVIDER_H @@ -49,7 +13,7 @@ QT_BEGIN_NAMESPACE // Implements the Selection Item control pattern provider. Used for List items and radio buttons. class QWindowsUiaSelectionItemProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<ISelectionItemProvider> + public QComObject<ISelectionItemProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaSelectionItemProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp index 3305e9c5c4..37148c655a 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) @@ -77,12 +41,24 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::GetSelection(SAFEARRAY * if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; - // First put selected items in a list, then build a safe array with the right size. + // First get/create list of selected items, then build a safe array with the right size. QList<QAccessibleInterface *> selectedList; - for (int i = 0; i < accessible->childCount(); ++i) { - if (QAccessibleInterface *child = accessible->child(i)) { - if (child->state().selected) { - selectedList.append(child); + if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) { + selectedList = selectionInterface->selectedItems(); + } else { + const int childCount = accessible->childCount(); + selectedList.reserve(childCount); + for (int i = 0; i < childCount; ++i) { + if (QAccessibleInterface *child = accessible->child(i)) { + if (accessible->role() == QAccessible::PageTabList) { + if (child->role() == QAccessible::PageTab && child->state().focused) { + selectedList.append(child); + } + } else { + if (child->state().selected) { + selectedList.append(child); + } + } } } } @@ -126,18 +102,155 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_IsSelectionRequired( if (!accessible) return UIA_E_ELEMENTNOTAVAILABLE; - // Initially returns false if none are selected. After the first selection, it may be required. - bool anySelected = false; - for (int i = 0; i < accessible->childCount(); ++i) { - if (QAccessibleInterface *child = accessible->child(i)) { - if (child->state().selected) { - anySelected = true; - break; + if (accessible->role() == QAccessible::PageTabList) { + *pRetVal = TRUE; + } else { + + // Initially returns false if none are selected. After the first selection, it may be required. + bool anySelected = false; + if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) { + anySelected = selectionInterface->selectedItem(0) != nullptr; + } else { + for (int i = 0; i < accessible->childCount(); ++i) { + if (QAccessibleInterface *child = accessible->child(i)) { + if (child->state().selected) { + anySelected = true; + break; + } + } + } + } + + *pRetVal = anySelected && !accessible->state().multiSelectable && !accessible->state().extSelectable; + } + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) +{ + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + + if (!pRetVal) + return E_INVALIDARG; + *pRetVal = nullptr; + + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible) + return UIA_E_ELEMENTNOTAVAILABLE; + + QAccessibleInterface *firstSelectedChild = nullptr; + if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) { + firstSelectedChild = selectionInterface->selectedItem(0); + if (!firstSelectedChild) + return UIA_E_ELEMENTNOTAVAILABLE; + } else { + int i = 0; + while (!firstSelectedChild && i < accessible->childCount()) { + if (QAccessibleInterface *child = accessible->child(i)) { + if (accessible->role() == QAccessible::PageTabList) { + if (child->role() == QAccessible::PageTab && child->state().focused) + firstSelectedChild = child; + } else if (child->state().selected) { + firstSelectedChild = child; + } + } + i++; + } + } + + if (!firstSelectedChild) + return UIA_E_ELEMENTNOTAVAILABLE; + + if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(firstSelectedChild)) + { + *pRetVal = static_cast<IRawElementProviderSimple *>(childProvider); + return S_OK; + } + + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) +{ + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + + if (!pRetVal) + return E_INVALIDARG; + *pRetVal = nullptr; + + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible) + return UIA_E_ELEMENTNOTAVAILABLE; + + QAccessibleInterface *lastSelectedChild = nullptr; + if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) { + const int selectedItemCount = selectionInterface->selectedItemCount(); + if (selectedItemCount <= 0) + return UIA_E_ELEMENTNOTAVAILABLE; + lastSelectedChild = selectionInterface->selectedItem(selectedItemCount - 1); + } else { + int i = accessible->childCount() - 1; + while (!lastSelectedChild && i >= 0) { + if (QAccessibleInterface *child = accessible->child(i)) { + if (accessible->role() == QAccessible::PageTabList) { + if (child->role() == QAccessible::PageTab && child->state().focused) + lastSelectedChild = child; + } else if (child->state().selected) { + lastSelectedChild = child; + } + } + i--; + } + } + + if (!lastSelectedChild) + return UIA_E_ELEMENTNOTAVAILABLE; + + if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(lastSelectedChild)) + { + *pRetVal = static_cast<IRawElementProviderSimple *>(childProvider); + return S_OK; + } + + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) +{ + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + return get_FirstSelectedItem(pRetVal); +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_ItemCount(__RPC__out int *pRetVal) +{ + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + + if (!pRetVal) + return E_INVALIDARG; + *pRetVal = -1; + + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible) + return UIA_E_ELEMENTNOTAVAILABLE; + + + if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) + *pRetVal = selectionInterface->selectedItemCount(); + else { + int selectedCount = 0; + for (int i = 0; i < accessible->childCount(); i++) { + if (QAccessibleInterface *child = accessible->child(i)) { + if (accessible->role() == QAccessible::PageTabList) { + if (child->role() == QAccessible::PageTab && child->state().focused) + selectedCount++; + } else if (child->state().selected) { + selectedCount++; + } } } + *pRetVal = selectedCount; } - *pRetVal = anySelected && !accessible->state().multiSelectable && !accessible->state().extSelectable; return S_OK; } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h index ee1044ec7a..7a899e4261 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIASELECTIONPROVIDER_H #define QWINDOWSUIASELECTIONPROVIDER_H @@ -47,9 +11,22 @@ QT_BEGIN_NAMESPACE +namespace QtPrivate { + +template <> +struct QComObjectTraits<ISelectionProvider2> +{ + static constexpr bool isGuidOf(REFIID riid) noexcept + { + return QComObjectTraits<ISelectionProvider2, ISelectionProvider>::isGuidOf(riid); + } +}; + +} // namespace QtPrivate + // Implements the Selection control pattern provider. Used for Lists. class QWindowsUiaSelectionProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<ISelectionProvider> + public QComObject<ISelectionProvider2> { Q_DISABLE_COPY_MOVE(QWindowsUiaSelectionProvider) public: @@ -60,6 +37,12 @@ public: HRESULT STDMETHODCALLTYPE GetSelection(SAFEARRAY **pRetVal) override; HRESULT STDMETHODCALLTYPE get_CanSelectMultiple(BOOL *pRetVal) override; HRESULT STDMETHODCALLTYPE get_IsSelectionRequired(BOOL *pRetVal) override; + + // ISelectionProvider2 + HRESULT STDMETHODCALLTYPE get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override; + HRESULT STDMETHODCALLTYPE get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override; + HRESULT STDMETHODCALLTYPE get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override; + HRESULT STDMETHODCALLTYPE get_ItemCount(__RPC__out int *pRetVal) override; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.cpp index 1348ec7cc0..f92c4b3af7 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h index 9804c35f8e..3bb0e1027e 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIATABLEITEMPROVIDER_H #define QWINDOWSUIATABLEITEMPROVIDER_H @@ -49,7 +13,7 @@ QT_BEGIN_NAMESPACE // Implements the Table Item control pattern provider. Used by items within a table/tree. class QWindowsUiaTableItemProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<ITableItemProvider> + public QComObject<ITableItemProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaTableItemProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.cpp index 80086f1d4f..9c7ede882d 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h index a8b16035ec..8beb11decf 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIATABLEPROVIDER_H #define QWINDOWSUIATABLEPROVIDER_H @@ -48,8 +12,7 @@ QT_BEGIN_NAMESPACE // Implements the Table control pattern provider. Used by tables/trees. -class QWindowsUiaTableProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<ITableProvider> +class QWindowsUiaTableProvider : public QWindowsUiaBaseProvider, public QComObject<ITableProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaTableProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp index 50ecfc7ecd..172836232e 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) @@ -62,20 +26,6 @@ QWindowsUiaTextProvider::~QWindowsUiaTextProvider() { } -HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::QueryInterface(REFIID iid, LPVOID *iface) -{ - qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this; - - if (!iface) - return E_INVALIDARG; - *iface = nullptr; - - const bool result = qWindowsComQueryUnknownInterfaceMulti<ITextProvider>(this, iid, iface) - || qWindowsComQueryInterface<ITextProvider>(this, iid, iface) - || qWindowsComQueryInterface<ITextProvider2>(this, iid, iface); - return result ? S_OK : E_NOINTERFACE; -} - // Returns an array of providers for the selected text ranges. HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetSelection(SAFEARRAY **pRetVal) { @@ -182,9 +132,10 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::RangeFromPoint(UiaPoint point nativeUiaPointToPoint(point, window, &pt); int offset = textInterface->offsetAtPoint(pt); - if ((offset >= 0) && (offset < textInterface->characterCount())) { - *pRetVal = new QWindowsUiaTextRangeProvider(id(), offset, offset); - } + if (offset < 0 || offset >= textInterface->characterCount()) + return UIA_E_ELEMENTNOTAVAILABLE; + + *pRetVal = new QWindowsUiaTextRangeProvider(id(), offset, offset); return S_OK; } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h index ffab48b0e8..8f886510b4 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIATEXTPROVIDER_H #define QWINDOWSUIATEXTPROVIDER_H @@ -48,18 +12,27 @@ QT_BEGIN_NAMESPACE +namespace QtPrivate { + +template <> +struct QComObjectTraits<ITextProvider2> +{ + static constexpr bool isGuidOf(REFIID riid) noexcept + { + return QComObjectTraits<ITextProvider2, ITextProvider>::isGuidOf(riid); + } +}; + +} // namespace QtPrivate + // Implements the Text control pattern provider. Used for text controls. -class QWindowsUiaTextProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<ITextProvider2> +class QWindowsUiaTextProvider : public QWindowsUiaBaseProvider, public QComObject<ITextProvider2> { Q_DISABLE_COPY_MOVE(QWindowsUiaTextProvider) public: explicit QWindowsUiaTextProvider(QAccessible::Id id); ~QWindowsUiaTextProvider(); - // IUnknown overrides - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override; - // ITextProvider HRESULT STDMETHODCALLTYPE GetSelection(SAFEARRAY **pRetVal) override; HRESULT STDMETHODCALLTYPE GetVisibleRanges(SAFEARRAY **pRetVal) override; diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp index 3465c55bc4..a62a33cfe2 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) @@ -203,6 +167,15 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextRangeProvider::GetAttributeValue(TEXTAT else setVariantI4(CaretPosition_Unknown, pRetVal); break; + case UIA_StrikethroughStyleAttributeId: + { + const QString value = valueForIA2Attribute(textInterface, QStringLiteral("text-line-through-type")); + if (value.isEmpty()) + break; + const TextDecorationLineStyle uiaLineStyle = uiaLineStyleForIA2LineStyle(value); + setVariantI4(uiaLineStyle, pRetVal); + break; + } default: break; } @@ -343,14 +316,14 @@ HRESULT QWindowsUiaTextRangeProvider::Move(TextUnit unit, int count, int *pRetVa int len = textInterface->characterCount(); - if (len < 1) + if (len < 1 || count == 0) // MSDN: "Zero has no effect." return S_OK; if (unit == TextUnit_Character) { // Moves the start point, ensuring it lies within the bounds. - int start = qBound(0, m_startOffset + count, len - 1); + int start = qBound(0, m_startOffset + count, len); // If range was initially empty, leaves it as is; otherwise, normalizes it to one char. - m_endOffset = (m_endOffset > m_startOffset) ? start + 1 : start; + m_endOffset = (m_endOffset > m_startOffset) ? qMin(start + 1, len) : start; *pRetVal = start - m_startOffset; // Returns the actually moved distance. m_startOffset = start; } else { @@ -421,7 +394,7 @@ HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByUnit(TextPatternRangeEndpoin if (unit == TextUnit_Character) { if (endpoint == TextPatternRangeEndpoint_Start) { - int boundedValue = qBound(0, m_startOffset + count, len - 1); + int boundedValue = qBound(0, m_startOffset + count, len); *pRetVal = boundedValue - m_startOffset; m_startOffset = boundedValue; m_endOffset = qBound(m_startOffset, m_endOffset, len); @@ -553,6 +526,42 @@ HRESULT QWindowsUiaTextRangeProvider::unselect() return S_OK; } +// helper method to retrieve the value of the given IAccessible2 text attribute, +// or an empty string if not set +QString QWindowsUiaTextRangeProvider::valueForIA2Attribute(QAccessibleTextInterface *textInterface, + const QString &key) +{ + Q_ASSERT(textInterface); + + int startOffset; + int endOffset; + const QString attributes = textInterface->attributes(m_startOffset, &startOffset, &endOffset); + // don't report if attributes don't apply for the whole range + if (startOffset > m_startOffset || endOffset < m_endOffset) + return {}; + + for (auto attr : QStringTokenizer{attributes, u';'}) + { + const QList<QStringView> items = attr.split(u':', Qt::SkipEmptyParts, Qt::CaseSensitive); + if (items.count() == 2 && items[0] == key) + return items[1].toString(); + } + + return {}; +} + +TextDecorationLineStyle QWindowsUiaTextRangeProvider::uiaLineStyleForIA2LineStyle(const QString &ia2LineStyle) +{ + if (ia2LineStyle == QStringLiteral("none")) + return TextDecorationLineStyle_None; + if (ia2LineStyle == QStringLiteral("single")) + return TextDecorationLineStyle_Single; + if (ia2LineStyle == QStringLiteral("double")) + return TextDecorationLineStyle_Double; + + return TextDecorationLineStyle_Other; +} + QT_END_NAMESPACE #endif // QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h index 3b4ec63ceb..a37429a603 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIATEXTRANGEPROVIDER_H #define QWINDOWSUIATEXTRANGEPROVIDER_H @@ -49,7 +13,7 @@ QT_BEGIN_NAMESPACE // Implements the Text Range control pattern provider. Used for text controls. class QWindowsUiaTextRangeProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<ITextRangeProvider> + public QComObject<ITextRangeProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaTextRangeProvider) public: @@ -78,6 +42,8 @@ public: private: HRESULT unselect(); + QString valueForIA2Attribute(QAccessibleTextInterface *textInterface, const QString &key); + TextDecorationLineStyle uiaLineStyleForIA2LineStyle(const QString &ia2LineStyle); int m_startOffset; int m_endOffset; }; diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.cpp index 32445e4ffb..694debd492 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h index 892b635b41..17150dfbe0 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIATOGGLEPROVIDER_H #define QWINDOWSUIATOGGLEPROVIDER_H @@ -48,8 +12,7 @@ QT_BEGIN_NAMESPACE // Implements the Toggle control pattern provider. Used for checkboxes. -class QWindowsUiaToggleProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<IToggleProvider> +class QWindowsUiaToggleProvider : public QWindowsUiaBaseProvider, public QComObject<IToggleProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaToggleProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp index 12bdc9e6b7..78ab3e890e 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) @@ -212,6 +176,9 @@ long roleToControlTypeId(QAccessible::Role role) {QAccessible::PageTabList, UIA_TabControlTypeId}, {QAccessible::Clock, UIA_CustomControlTypeId}, {QAccessible::Splitter, UIA_CustomControlTypeId}, + {QAccessible::Paragraph, UIA_TextControlTypeId}, + {QAccessible::WebDocument, UIA_DocumentControlTypeId}, + {QAccessible::Heading, UIA_TextControlTypeId}, }; return mapping.value(role, UIA_CustomControlTypeId); diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h index 0daef2ea63..bf90211cec 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAUTILS_H #define QWINDOWSUIAUTILS_H @@ -48,7 +12,7 @@ #include <QtGui/qaccessible.h> #include <QtGui/qwindow.h> #include <QtCore/qrect.h> -#include <QtGui/private/qwindowsuiawrapper_p.h> +#include "qwindowsuiautomation.h" QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp new file mode 100644 index 0000000000..1593a07202 --- /dev/null +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtGui/qtguiglobal.h> +#if QT_CONFIG(accessibility) + +#include "qwindowsuiautomation.h" + +#if defined(__MINGW32__) || defined(__MINGW64__) + +template<typename T, typename... TArg> +struct winapi_func +{ + using func_t = T(WINAPI*)(TArg...); + const func_t func; + const T error_value; +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + winapi_func(const char *lib_name, const char *func_name, func_t func_proto, + T error_value = T(__HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND))) : + func(reinterpret_cast<func_t>(GetProcAddress(LoadLibraryA(lib_name), func_name))), + error_value(error_value) + { + std::ignore = func_proto; + } +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + T invoke(TArg... arg) + { + if (!func) + return error_value; + return func(arg...); + } +}; + +#define FN(fn) #fn,fn + +BOOL WINAPI UiaClientsAreListening() +{ + static auto func = winapi_func("uiautomationcore", FN(UiaClientsAreListening), BOOL(false)); + return func.invoke(); +} + +LRESULT WINAPI UiaReturnRawElementProvider( + HWND hwnd, WPARAM wParam, LPARAM lParam, IRawElementProviderSimple *el) +{ + static auto func = winapi_func("uiautomationcore", FN(UiaReturnRawElementProvider)); + return func.invoke(hwnd, wParam, lParam, el); +} + +HRESULT WINAPI UiaHostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider) +{ + static auto func = winapi_func("uiautomationcore", FN(UiaHostProviderFromHwnd)); + return func.invoke(hwnd, ppProvider); +} + +HRESULT WINAPI UiaRaiseAutomationPropertyChangedEvent( + IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue) +{ + static auto func = winapi_func("uiautomationcore", FN(UiaRaiseAutomationPropertyChangedEvent)); + return func.invoke(pProvider, id, oldValue, newValue); +} + +HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id) +{ + static auto func = winapi_func("uiautomationcore", FN(UiaRaiseAutomationEvent)); + return func.invoke(pProvider, id); +} + +#endif // defined(__MINGW32__) || defined(__MINGW64__) + +#endif // QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h new file mode 100644 index 0000000000..a192b9b0fb --- /dev/null +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h @@ -0,0 +1,70 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSUIAUTOMATION_H +#define QWINDOWSUIAUTOMATION_H + +#include <QtGui/qtguiglobal.h> +#if QT_CONFIG(accessibility) + +#include <uiautomation.h> + +#if defined(__MINGW32__) || defined(__MINGW64__) + +#define UIA_SelectionPattern2Id 10034 +#define UIA_IsReadOnlyAttributeId 40015 +#define UIA_StrikethroughStyleAttributeId 40026 +#define UIA_CaretPositionAttributeId 40038 + +enum CaretPosition { + CaretPosition_Unknown = 0, + CaretPosition_EndOfLine = 1, + CaretPosition_BeginningOfLine = 2 +}; + +enum TextDecorationLineStyle { + TextDecorationLineStyle_None = 0, + TextDecorationLineStyle_Single = 1, + TextDecorationLineStyle_WordsOnly = 2, + TextDecorationLineStyle_Double = 3, + TextDecorationLineStyle_Dot = 4, + TextDecorationLineStyle_Dash = 5, + TextDecorationLineStyle_DashDot = 6, + TextDecorationLineStyle_DashDotDot = 7, + TextDecorationLineStyle_Wavy = 8, + TextDecorationLineStyle_ThickSingle = 9, + TextDecorationLineStyle_DoubleWavy = 11, + TextDecorationLineStyle_ThickWavy = 12, + TextDecorationLineStyle_LongDash = 13, + TextDecorationLineStyle_ThickDash = 14, + TextDecorationLineStyle_ThickDashDot = 15, + TextDecorationLineStyle_ThickDashDotDot = 16, + TextDecorationLineStyle_ThickDot = 17, + TextDecorationLineStyle_ThickLongDash = 18, + TextDecorationLineStyle_Other = -1 +}; + +BOOL WINAPI UiaClientsAreListening(); + +#ifndef __ISelectionProvider2_INTERFACE_DEFINED__ +#define __ISelectionProvider2_INTERFACE_DEFINED__ +DEFINE_GUID(IID_ISelectionProvider2, 0x14f68475, 0xee1c, 0x44f6, 0xa8, 0x69, 0xd2, 0x39, 0x38, 0x1f, 0x0f, 0xe7); +MIDL_INTERFACE("14f68475-ee1c-44f6-a869-d239381f0fe7") +ISelectionProvider2 : public ISelectionProvider +{ +public: + virtual HRESULT STDMETHODCALLTYPE get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0; + virtual HRESULT STDMETHODCALLTYPE get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0; + virtual HRESULT STDMETHODCALLTYPE get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0; + virtual HRESULT STDMETHODCALLTYPE get_ItemCount(__RPC__out int *retVal) = 0; +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(ISelectionProvider2, 0x14f68475, 0xee1c, 0x44f6, 0xa8, 0x69, 0xd2, 0x39, 0x38, 0x1f, 0x0f, 0xe7) +#endif +#endif // __ISelectionProvider2_INTERFACE_DEFINED__ + +#endif // defined(__MINGW32__) || defined(__MINGW64__) + +#endif // QT_CONFIG(accessibility) + +#endif // QWINDOWSUIAUTOMATION_H diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.cpp index 9b41162d45..77f40cc2cc 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h index 3edfe7a08b..8c0a6b8ee7 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAVALUEPROVIDER_H #define QWINDOWSUIAVALUEPROVIDER_H @@ -49,8 +13,7 @@ QT_BEGIN_NAMESPACE // Implements the Value control pattern provider. // Supported for all controls that can return text(QAccessible::Value). -class QWindowsUiaValueProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<IValueProvider> +class QWindowsUiaValueProvider : public QWindowsUiaBaseProvider, public QComObject<IValueProvider> { Q_DISABLE_COPY_MOVE(QWindowsUiaValueProvider) public: diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp index 3738aa72ff..d86face0db 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtguiglobal.h> #if QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h index 343fb275f7..89a1655232 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSUIAWINDOWPROVIDER_H #define QWINDOWSUIAWINDOWPROVIDER_H @@ -47,8 +11,7 @@ QT_BEGIN_NAMESPACE -class QWindowsUiaWindowProvider : public QWindowsUiaBaseProvider, - public QWindowsComBase<IWindowProvider> +class QWindowsUiaWindowProvider : public QWindowsUiaBaseProvider, public QComObject<IWindowProvider> { Q_DISABLE_COPY(QWindowsUiaWindowProvider) public: |