diff options
Diffstat (limited to 'src/plugins/platforms/windows')
119 files changed, 5940 insertions, 8479 deletions
diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt new file mode 100644 index 0000000000..4b92317978 --- /dev/null +++ b/src/plugins/platforms/windows/CMakeLists.txt @@ -0,0 +1,213 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## QWindowsIntegrationPlugin Plugin: +##################################################################### + +qt_internal_add_plugin(QWindowsIntegrationPlugin + OUTPUT_NAME qwindows + PLUGIN_TYPE platforms + 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 + 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 + qwindowsmimeregistry.cpp qwindowsmimeregistry.h + qwindowsmousehandler.cpp qwindowsmousehandler.h + qwindowsnativeinterface.cpp qwindowsnativeinterface.h + qwindowsole.cpp qwindowsole.h + qwindowsopengltester.cpp qwindowsopengltester.h + qwindowspointerhandler.cpp qwindowspointerhandler.h + qwindowsscreen.cpp qwindowsscreen.h + qwindowsservices.cpp qwindowsservices.h + 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 + INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_SOURCE_DIR} + LIBRARIES + Qt::Core + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + advapi32 + dwmapi + gdi32 + imm32 + ole32 + oleaut32 + setupapi + shell32 + shlwapi + user32 + winmm + winspool + wtsapi32 + shcore + comdlg32 + d3d9 + runtimeobject +) + +# Resources: +set_source_files_properties("openglblacklists/default.json" + PROPERTIES QT_RESOURCE_ALIAS "default.json" +) +set(openglblacklists_resource_files + "openglblacklists/default.json" +) + +qt_internal_add_resource(QWindowsIntegrationPlugin "openglblacklists" + PREFIX + "/qt-project.org/windows/openglblacklists" + FILES + ${openglblacklists_resource_files} +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_opengl + SOURCES + qwindowsglcontext.cpp qwindowsglcontext.h + qwindowsopenglcontext.h + LIBRARIES + Qt::OpenGLPrivate +) + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_opengl AND NOT QT_FEATURE_dynamicgl + LIBRARIES + opengl32 +) + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION MINGW + LIBRARIES + uuid + NO_PCH_SOURCES + qwindowspointerhandler.cpp +) + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_systemtrayicon + SOURCES + qwindowssystemtrayicon.cpp qwindowssystemtrayicon.h +) + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_vulkan + SOURCES + qwindowsvulkaninstance.cpp qwindowsvulkaninstance.h +) + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_clipboard + SOURCES + qwindowsclipboard.cpp qwindowsclipboard.h +) + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_clipboard AND QT_FEATURE_draganddrop + SOURCES + qwindowsdrag.cpp qwindowsdrag.h +) + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_tabletevent + SOURCES + qwindowstabletsupport.cpp qwindowstabletsupport.h + INCLUDE_DIRECTORIES + ${QtBase_SOURCE_DIR}/src/3rdparty/wintab +) + +qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_sessionmanager + SOURCES + qwindowssessionmanager.cpp qwindowssessionmanager.h +) + +if(QT_FEATURE_imageformat_png) + # Resources: + set(cursors_resource_files + "images/closedhandcursor_32.png" + "images/closedhandcursor_48.png" + "images/closedhandcursor_64.png" + "images/dragcopycursor_32.png" + "images/dragcopycursor_48.png" + "images/dragcopycursor_64.png" + "images/draglinkcursor_32.png" + "images/draglinkcursor_48.png" + "images/draglinkcursor_64.png" + "images/dragmovecursor_32.png" + "images/dragmovecursor_48.png" + "images/dragmovecursor_64.png" + "images/openhandcursor_32.png" + "images/openhandcursor_48.png" + "images/openhandcursor_64.png" + "images/splithcursor_32.png" + "images/splithcursor_48.png" + "images/splithcursor_64.png" + "images/splitvcursor_32.png" + "images/splitvcursor_48.png" + "images/splitvcursor_64.png" + ) + + qt_internal_add_resource(QWindowsIntegrationPlugin "cursors" + PREFIX + "/qt-project.org/windows/cursors" + FILES + ${cursors_resource_files} + ) +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 + uiautomation/qwindowsuiagriditemprovider.cpp uiautomation/qwindowsuiagriditemprovider.h + uiautomation/qwindowsuiagridprovider.cpp uiautomation/qwindowsuiagridprovider.h + uiautomation/qwindowsuiainvokeprovider.cpp uiautomation/qwindowsuiainvokeprovider.h + uiautomation/qwindowsuiamainprovider.cpp uiautomation/qwindowsuiamainprovider.h + uiautomation/qwindowsuiaprovidercache.cpp uiautomation/qwindowsuiaprovidercache.h + uiautomation/qwindowsuiarangevalueprovider.cpp uiautomation/qwindowsuiarangevalueprovider.h + uiautomation/qwindowsuiaselectionitemprovider.cpp uiautomation/qwindowsuiaselectionitemprovider.h + uiautomation/qwindowsuiaselectionprovider.cpp uiautomation/qwindowsuiaselectionprovider.h + uiautomation/qwindowsuiatableitemprovider.cpp uiautomation/qwindowsuiatableitemprovider.h + uiautomation/qwindowsuiatableprovider.cpp uiautomation/qwindowsuiatableprovider.h + uiautomation/qwindowsuiatextprovider.cpp uiautomation/qwindowsuiatextprovider.h + uiautomation/qwindowsuiatextrangeprovider.cpp uiautomation/qwindowsuiatextrangeprovider.h + uiautomation/qwindowsuiatoggleprovider.cpp uiautomation/qwindowsuiatoggleprovider.h + uiautomation/qwindowsuiautils.cpp uiautomation/qwindowsuiautils.h + uiautomation/qwindowsuiavalueprovider.cpp uiautomation/qwindowsuiavalueprovider.h + 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 +) 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 1929f800a4..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,11 +9,12 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + /*! - \group qt-lighthouse-win - \title Qt Lighthouse plugin for Windows + \title Qt platform plugin for Windows - \brief Class documentation of the Qt Lighthouse plugin for Windows. + \brief Class documentation of the Qt platform plugin for Windows. \section1 Supported Parameters @@ -78,7 +43,6 @@ QT_BEGIN_NAMESPACE \class QWindowsIntegrationPlugin \brief Plugin. \internal - \ingroup qt-lighthouse-win */ /*! @@ -86,7 +50,6 @@ QT_BEGIN_NAMESPACE \brief Namespace for enumerations, etc. \internal - \ingroup qt-lighthouse-win */ /*! @@ -97,7 +60,6 @@ QT_BEGIN_NAMESPACE With flags that should help to structure the code. \internal - \ingroup qt-lighthouse-win */ class QWindowsIntegrationPlugin : public QPlatformIntegrationPlugin @@ -105,12 +67,12 @@ class QWindowsIntegrationPlugin : public QPlatformIntegrationPlugin Q_OBJECT Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "windows.json") public: - QPlatformIntegration *create(const QString&, const QStringList&, int &, char **); + QPlatformIntegration *create(const QString &, const QStringList &, int &, char **) override; }; 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 985f13bdc5..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 @@ -76,12 +52,20 @@ # define WM_POINTERHWHEEL 0x024F #endif // WM_POINTERUPDATE +#if !defined(_DPI_AWARENESS_CONTEXTS_) +# define DPI_AWARENESS_CONTEXT_UNAWARE ((HANDLE)-1) +# define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((HANDLE)-2) +# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((HANDLE)-3) +# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE)-4) +# define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((HANDLE)-5) +#endif + QT_BEGIN_NAMESPACE namespace QtWindows { -enum +enum WindowsEventTypeFlags { WindowEventFlag = 0x10000, MouseEventFlag = 0x20000, @@ -121,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, @@ -132,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, @@ -154,26 +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 + 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: @@ -232,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; @@ -271,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; @@ -307,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: @@ -323,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 new file mode 100644 index 0000000000..42e34ac99f --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsapplication.cpp @@ -0,0 +1,146 @@ +// 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 "qwindowsmimeregistry.h" +#include "qwin10helpers.h" +#include "qwindowsopengltester.h" +#include "qwindowswindow.h" +#include "qwindowsintegration.h" +#include "qwindowstheme.h" + +#include <QtCore/qvariant.h> +#include <QtCore/private/qfunctions_win_p.h> + +#include <QtGui/qpalette.h> + +QT_BEGIN_NAMESPACE + +void QWindowsApplication::setTouchWindowTouchType(QWindowsApplication::TouchWindowTouchTypes type) +{ + if (m_touchWindowTouchTypes == type) + return; + m_touchWindowTouchTypes = type; + if (auto ctx = QWindowsContext::instance()) + ctx->registerTouchWindows(); +} + +QWindowsApplication::TouchWindowTouchTypes QWindowsApplication::touchWindowTouchType() const +{ + return m_touchWindowTouchTypes; +} + +QWindowsApplication::WindowActivationBehavior QWindowsApplication::windowActivationBehavior() const +{ + return m_windowActivationBehavior; +} + +void QWindowsApplication::setWindowActivationBehavior(WindowActivationBehavior behavior) +{ + m_windowActivationBehavior = behavior; +} + +void QWindowsApplication::setHasBorderInFullScreenDefault(bool border) +{ + QWindowsWindow::setHasBorderInFullScreenDefault(border); +} + +bool QWindowsApplication::isTabletMode() const +{ +#if QT_CONFIG(clipboard) + if (const QWindowsClipboard *clipboard = QWindowsClipboard::instance()) + return qt_windowsIsTabletMode(clipboard->clipboardViewer()); +#endif + return false; +} + +bool QWindowsApplication::isWinTabEnabled() const +{ + auto ctx = QWindowsContext::instance(); + return ctx != nullptr && ctx->tabletSupport() != nullptr; +} + +bool QWindowsApplication::setWinTabEnabled(bool enabled) +{ + if (enabled == isWinTabEnabled()) + return true; + auto ctx = QWindowsContext::instance(); + if (!ctx) + return false; + return enabled ? ctx->initTablet() : ctx->disposeTablet(); +} + +QWindowsApplication::DarkModeHandling QWindowsApplication::darkModeHandling() const +{ + return m_darkModeHandling; +} + +void QWindowsApplication::setDarkModeHandling(QWindowsApplication::DarkModeHandling handling) +{ + m_darkModeHandling = handling; +} + +void QWindowsApplication::registerMime(QWindowsMimeConverter *mime) +{ + if (auto ctx = QWindowsContext::instance()) + ctx->mimeConverter().registerMime(mime); +} + +void QWindowsApplication::unregisterMime(QWindowsMimeConverter *mime) +{ + if (auto ctx = QWindowsContext::instance()) + ctx->mimeConverter().unregisterMime(mime); +} + +int QWindowsApplication::registerMimeType(const QString &mime) +{ + return QWindowsMimeRegistry::registerMimeType(mime); +} + +HWND QWindowsApplication::createMessageWindow(const QString &classNameTemplate, + const QString &windowName, + QFunctionPointer eventProc) const +{ + QWindowsContext *ctx = QWindowsContext::instance(); + if (!ctx) + return nullptr; + auto wndProc = eventProc ? reinterpret_cast<WNDPROC>(eventProc) : DefWindowProc; + return ctx->createDummyWindow(classNameTemplate, + reinterpret_cast<const wchar_t*>(windowName.utf16()), + wndProc); +} + +bool QWindowsApplication::asyncExpose() const +{ + QWindowsContext *ctx = QWindowsContext::instance(); + return ctx && ctx->asyncExpose(); +} + +void QWindowsApplication::setAsyncExpose(bool value) +{ + if (QWindowsContext *ctx = QWindowsContext::instance()) + ctx->setAsyncExpose(value); +} + +QVariant QWindowsApplication::gpu() const +{ + return GpuDescription::detect().toVariant(); +} + +QVariant QWindowsApplication::gpuList() const +{ + QVariantList result; + const auto gpus = GpuDescription::detectAll(); + for (const auto &gpu : gpus) + result.append(gpu.toVariant()); + 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 new file mode 100644 index 0000000000..0918df91af --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsapplication.h @@ -0,0 +1,55 @@ +// 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 + +#include <QtGui/private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +class QWindowsApplication : public QNativeInterface::Private::QWindowsApplication +{ +public: + void setTouchWindowTouchType(TouchWindowTouchTypes type) override; + TouchWindowTouchTypes touchWindowTouchType() const override; + + 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; + + DarkModeHandling darkModeHandling() const override; + void setDarkModeHandling(DarkModeHandling handling) override; + + void registerMime(QWindowsMimeConverter *mime) override; + void unregisterMime(QWindowsMimeConverter *mime) override; + + int registerMimeType(const QString &mime) override; + + HWND createMessageWindow(const QString &classNameTemplate, + const QString &windowName, + QFunctionPointer eventProc = nullptr) const override; + + bool asyncExpose() const override; + void setAsyncExpose(bool value) override; + + QVariant gpu() const override; + QVariant gpuList() const override; + + void populateLightSystemPalette(QPalette &palette) const override; + +private: + WindowActivationBehavior m_windowActivationBehavior = DefaultActivateWindow; + TouchWindowTouchTypes m_touchWindowTouchTypes = NormalTouch; + DarkModeHandling m_darkModeHandling; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSAPPLICATION_H diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp index bd7bdc55d1..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" @@ -43,7 +7,7 @@ #include <QtGui/qwindow.h> #include <QtGui/qpainter.h> -#include <QtFontDatabaseSupport/private/qwindowsnativeimage_p.h> +#include <QtGui/private/qwindowsnativeimage_p.h> #include <private/qhighdpiscaling_p.h> #include <private/qimage_p.h> @@ -55,7 +19,6 @@ QT_BEGIN_NAMESPACE \class QWindowsBackingStore \brief Backing store for windows. \internal - \ingroup qt-lighthouse-win */ QWindowsBackingStore::QWindowsBackingStore(QWindow *window) : @@ -92,9 +55,8 @@ void QWindowsBackingStore::flush(QWindow *window, const QRegion ®ion, if ((flags & Qt::FramelessWindowHint) && QWindowsWindow::setWindowLayered(rw->handle(), flags, hasAlpha, rw->opacity()) && hasAlpha) { // Windows with alpha: Use blend function to update. QRect r = QHighDpi::toNativePixels(window->frameGeometry(), window); - QPoint frameOffset(QHighDpi::toNativePixels(QPoint(window->frameMargins().left(), window->frameMargins().top()), - static_cast<const QWindow *>(nullptr))); - QRect dirtyRect = br.translated(offset + frameOffset); + QMargins frameMargins = rw->frameMargins(); + QRect dirtyRect = br.translated(offset + QPoint(frameMargins.left(), frameMargins.top())); SIZE size = {r.width(), r.height()}; POINT ptDst = {r.x(), r.y()}; @@ -155,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); @@ -184,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 4e6d3306e1..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,8 +16,9 @@ #include <QtCore/qthread.h> #include <QtCore/qvariant.h> #include <QtCore/qurl.h> +#include <QtCore/private/qsystemerror_p.h> -#include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h> +#include <QtGui/private/qwindowsguieventdispatcher_p.h> QT_BEGIN_NAMESPACE @@ -71,7 +35,6 @@ QT_BEGIN_NAMESPACE \note The OLE-functions used in this class require OleInitialize(). \internal - \ingroup qt-lighthouse-win */ #ifndef QT_NO_DEBUG_STREAM @@ -82,7 +45,7 @@ static QDebug operator<<(QDebug d, const QMimeData *mimeData) d << "QMimeData("; if (mimeData) { const QStringList formats = mimeData->formats(); - d << "formats=" << formats.join(QLatin1String(", ")); + d << "formats=" << formats.join(u", "); if (mimeData->hasText()) d << ", text=" << mimeData->text(); if (mimeData->hasHtml()) @@ -110,7 +73,6 @@ static QDebug operator<<(QDebug d, const QMimeData *mimeData) \sa QWindowsInternalMimeDataBase, QWindowsClipboard \internal - \ingroup qt-lighthouse-win */ IDataObject *QWindowsClipboardRetrievalMimeData::retrieveDataObject() const @@ -190,15 +152,12 @@ void QWindowsClipboard::releaseIData() void QWindowsClipboard::registerViewer() { m_clipboardViewer = QWindowsContext::instance()-> - createDummyWindow(QStringLiteral("Qt5ClipboardView"), L"Qt5ClipboardView", + 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); @@ -212,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); @@ -339,10 +298,10 @@ void QWindowsClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) if (src != S_OK) { QString mimeDataFormats = mimeData ? - mimeData->formats().join(QLatin1String(", ")) : QString(QStringLiteral("NULL")); + 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 45cba9c68b..0000000000 --- a/src/plugins/platforms/windows/qwindowscombase.h +++ /dev/null @@ -1,112 +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) - { - *iface = nullptr; - return qWindowsComQueryInterface<IUnknown>(this, id, iface) || qWindowsComQueryInterface<ComInterface>(this, id, iface) - ? S_OK : E_NOINTERFACE; - } - - ULONG STDMETHODCALLTYPE AddRef() { return ++m_ref; } - - ULONG STDMETHODCALLTYPE Release() - { - if (!--m_ref) { - delete this; - return 0; - } - return m_ref; - } - -private: - ULONG m_ref; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSCOMBASE_H diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index a2dd25f8cc..de65a2171d 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -1,52 +1,17 @@ -/**************************************************************************** -** -** 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" #include "qwindowswindow.h" #include "qwindowskeymapper.h" +#include "qwindowsnativeinterface.h" #include "qwindowsmousehandler.h" #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" @@ -69,30 +34,37 @@ #include <qpa/qplatformnativeinterface.h> #include <QtGui/qguiapplication.h> #include <QtGui/qopenglcontext.h> +#include <QtGui/qpointingdevice.h> #include <QtCore/qset.h> #include <QtCore/qhash.h> +#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 <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_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") @@ -103,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; @@ -148,98 +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 - \ingroup qt-lighthouse-win -*/ - -void QWindowsUser32DLL::init() -{ - QSystemLibrary library(QStringLiteral("user32")); - setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware"); - - addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener"); - removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener"); - - getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences"); - setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences"); - - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8) { - 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"); - } -} - -bool QWindowsUser32DLL::supportsPointerApi() -{ - return enableMouseInPointer && getPointerType && getPointerInfo && getPointerDeviceRects - && getPointerTouchInfo && getPointerFrameTouchInfo && getPointerFrameTouchInfoHistory - && getPointerPenInfo && getPointerPenInfoHistory && skipPointerFrameMessages; -} - -void QWindowsShcoreDLL::init() -{ - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1) - return; - 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; /*! @@ -249,23 +130,20 @@ QWindowsContext *QWindowsContext::m_instance = nullptr; Holds state information formerly stored in \c qapplication_win.cpp. \internal - \ingroup qt-lighthouse-win */ -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) @@ -276,14 +154,14 @@ struct QWindowsContextPrivate { bool m_asyncExpose = false; HPOWERNOTIFY m_powerNotification = nullptr; HWND m_powerDummyWindow = nullptr; + static bool m_v2DpiAware; }; +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); @@ -294,7 +172,7 @@ QWindowsContextPrivate::QWindowsContextPrivate() } if (FAILED(m_oleInitializeResult)) { qWarning() << "QWindowsContext: OleInitialize() failed: " - << QWindowsContext::comErrorString(m_oleInitializeResult); + << QSystemError::windowsComString(m_oleInitializeResult); } } @@ -324,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) @@ -342,33 +224,50 @@ bool QWindowsContext::initTouch(unsigned integrationOptions) { if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) return true; - - QTouchDevice *touchDevice = (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ? - d->m_pointerHandler.ensureTouchDevice() : d->m_mouseHandler.ensureTouchDevice(); - if (!touchDevice) + const bool usePointerHandler = (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) != 0; + auto touchDevice = usePointerHandler ? d->m_pointerHandler.touchDevice() : d->m_mouseHandler.touchDevice(); + if (touchDevice.isNull()) { + const bool mouseEmulation = + (integrationOptions & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch) == 0; + touchDevice = QWindowsPointerHandler::createTouchDevice(mouseEmulation); + } + if (touchDevice.isNull()) return false; - - if (!(integrationOptions & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch)) - touchDevice->setCapabilities(touchDevice->capabilities() | QTouchDevice::MouseEmulation); - - QWindowSystemInterface::registerTouchDevice(touchDevice); + d->m_pointerHandler.setTouchDevice(touchDevice); + d->m_mouseHandler.setTouchDevice(touchDevice); + QWindowSystemInterface::registerInputDevice(touchDevice.data()); d->m_systemInfo |= QWindowsContext::SI_SupportsTouch; // A touch device was plugged while the app is running. Register all windows for touch. - if (QGuiApplicationPrivate::is_app_running) { - for (QWindowsWindow *w : qAsConst(d->m_windows)) + registerTouchWindows(); + + return true; +} + +void QWindowsContext::registerTouchWindows() +{ + if (QGuiApplicationPrivate::is_app_running + && (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) != 0) { + for (QWindowsWindow *w : std::as_const(d->m_windows)) w->registerTouchWindow(); } +} +bool QWindowsContext::initTablet() +{ +#if QT_CONFIG(tabletevent) + d->m_tabletSupport.reset(QWindowsTabletSupport::create()); return true; +#else + return false; +#endif } -bool QWindowsContext::initTablet(unsigned integrationOptions) +bool QWindowsContext::disposeTablet() { - Q_UNUSED(integrationOptions); #if QT_CONFIG(tabletevent) - d->m_tabletSupport.reset(QWindowsTabletSupport::create()); + d->m_tabletSupport.reset(); return true; #else return false; @@ -380,12 +279,6 @@ bool QWindowsContext::initPointer(unsigned integrationOptions) if (integrationOptions & QWindowsIntegration::DontUseWMPointer) return false; - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) - return false; - - if (!QWindowsContext::user32dll.supportsPointerApi()) - return false; - d->m_systemInfo |= QWindowsContext::SI_SupportsPointer; return true; } @@ -426,7 +319,7 @@ bool QWindowsContext::initPowerNotificationHandler() if (d->m_powerNotification) return false; - d->m_powerDummyWindow = createDummyWindow(QStringLiteral("QtPowerDummyWindow"), L"QtPowerDummyWindow", qWindowsPowerWindowProc); + d->m_powerDummyWindow = createDummyWindow(QStringLiteral("PowerDummyWindow"), L"QtPowerDummyWindow", qWindowsPowerWindowProc); if (!d->m_powerDummyWindow) return false; @@ -442,10 +335,9 @@ bool QWindowsContext::initPowerNotificationHandler() void QWindowsContext::setTabletAbsoluteRange(int a) { #if QT_CONFIG(tabletevent) - if (!d->m_tabletSupport.isNull()) - d->m_tabletSupport->setAbsoluteRange(a); + QWindowsTabletSupport::setAbsoluteRange(a); #else - Q_UNUSED(a) + Q_UNUSED(a); #endif } @@ -454,34 +346,119 @@ void QWindowsContext::setDetectAltGrModifier(bool a) d->m_keyMapper.setDetectAltGrModifier(a); } -int QWindowsContext::processDpiAwareness() +[[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 nullptr; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, QtWindows::DpiAwareness dpiAwareness) { - int result; - if (QWindowsContext::shcoredll.getProcessDpiAwareness - && SUCCEEDED(QWindowsContext::shcoredll.getProcessDpiAwareness(nullptr, &result))) { - return result; + 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; } - return -1; + d.nospace().noquote() << message; + return d; } +#endif -void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness) +bool QWindowsContext::setProcessDpiAwareness(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"); - } + 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; + } + 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() @@ -499,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; +} + +QWindowsContext::HandleBaseWindowHash &QWindowsContext::windows() { - return d->m_keyMapper.possibleKeys(e); + return d->m_windows; } QSharedPointer<QWindowCreationContext> QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx) @@ -536,6 +518,25 @@ void QWindowsContext::setKeyGrabber(QWindow *w) d->m_keyMapper.setKeyGrabber(w); } +QString QWindowsContext::classNamePrefix() +{ + static QString result; + if (result.isEmpty()) { + QTextStream str(&result); + str << "Qt" << QT_VERSION_MAJOR << QT_VERSION_MINOR << QT_VERSION_PATCH; + if (QLibraryInfo::isDebugBuild()) + str << 'd'; +#ifdef QT_NAMESPACE +# define xstr(s) str(s) +# define str(s) #s + str << xstr(QT_NAMESPACE); +# undef str +# undef xstr +#endif + } + return result; +} + // Window class registering code (from qapplication_win.cpp) QString QWindowsContext::registerWindowClass(const QWindow *w) @@ -567,31 +568,31 @@ QString QWindowsContext::registerWindowClass(const QWindow *w) break; } // Create a unique name for the flag combination - QString cname; - cname += QLatin1String("Qt5QWindow"); + QString cname = classNamePrefix(); + 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, @@ -604,15 +605,13 @@ QString QWindowsContext::registerWindowClass(QString cname, // each one has to have window class names with a unique name // The first instance gets the unmodified name; if the class // has already been registered by another instance of Qt then - // add a UUID. - static int classExists = -1; - + // add a UUID. The check needs to be performed for each name + // in case new message windows are added (QTBUG-81347). + // Note: GetClassInfo() returns != 0 when a class exists. const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr)); - if (classExists == -1) { - WNDCLASS wcinfo; - classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(cname.utf16()), &wcinfo); - classExists = classExists && wcinfo.lpfnWndProc != proc; - } + WNDCLASS wcinfo; + const bool classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(cname.utf16()), &wcinfo) != FALSE + && wcinfo.lpfnWndProc != proc; if (classExists) cname += QUuid::createUuid().toString(); @@ -652,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; @@ -662,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)); } @@ -674,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); - ushort *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); @@ -794,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; @@ -851,7 +835,7 @@ bool QWindowsContext::isSessionLocked() return result; } -QWindowsMimeConverter &QWindowsContext::mimeConverter() const +QWindowsMimeRegistry &QWindowsContext::mimeConverter() const { return d->m_mimeConverter; } @@ -881,7 +865,7 @@ HWND QWindowsContext::createDummyWindow(const QString &classNameIn, { if (!wndProc) wndProc = DefWindowProc; - QString className = registerWindowClass(classNameIn, wndProc); + QString className = registerWindowClass(classNamePrefix() + classNameIn, wndProc); return CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()), windowName, style, CW_USEDEFAULT, CW_USEDEFAULT, @@ -889,103 +873,18 @@ 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) +void QWindowsContext::forceNcCalcSize(HWND hwnd) { - 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); - result += ')'; - return result; + // Force WM_NCCALCSIZE to adjust margin + SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); } 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; } @@ -1026,31 +925,13 @@ static inline QWindowsInputContext *windowsInputContext() return qobject_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext()); } - -// Child windows, fixed-size windows or pop-ups and similar should not be resized -static inline bool resizeOnDpiChanged(const QWindow *w) -{ - bool result = false; - if (w->isTopLevel()) { - switch (w->type()) { - case Qt::Window: - case Qt::Dialog: - case Qt::Sheet: - case Qt::Drawer: - case Qt::Tool: - result = !w->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint); - break; - default: - break; - } - } - return result; -} - bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window) { - return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 - && window->isTopLevel() + // DPI aware V2 processes always have NonClientDpiScaling enabled. + if (QWindowsContextPrivate::m_v2DpiAware) + return true; + + return window->isTopLevel() && !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid() #if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL && (window->surfaceType() != QSurface::OpenGLSurface @@ -1086,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. @@ -1102,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); @@ -1183,14 +1080,15 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, #else return false; #endif - case QtWindows::DisplayChangedEvent: - if (QWindowsTheme *t = QWindowsTheme::instance()) - t->displayChanged(); - QWindowsWindow::displayChanged(); - return d->m_screenManager.handleDisplayChange(wParam, lParam); - case QtWindows::SettingChangedEvent: + case QtWindows::SettingChangedEvent: { QWindowsWindow::settingsChanged(); + // 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(); + } default: break; } @@ -1204,14 +1102,18 @@ 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)); return true; case QtWindows::NonClientCreate: - if (shouldHaveNonClientDpiScaling(d->m_creationContext->window)) - enableNonClientDpiScaling(msg.hwnd); + if (shouldHaveNonClientDpiScaling(d->m_creationContext->window) && + // DPI aware V2 processes always have NonClientDpiScaling enabled + // and there is no need to make an API call to manually enable. + !QWindowsContextPrivate::m_v2DpiAware) { + enableNonClientDpiScaling(msg.hwnd); + } return false; case QtWindows::CalculateSize: return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result); @@ -1243,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(); @@ -1273,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)); @@ -1283,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)) @@ -1347,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()); @@ -1388,26 +1299,15 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; #endif } break; - case QtWindows::DpiChangedEvent: { - // Try to apply the suggested size first and then notify ScreenChanged - // so that the resize event sent from QGuiApplication incorporates it - // WM_DPICHANGED is sent with a size that avoids resize loops (by - // snapping back to the previous screen, see QTBUG-65580). - const bool doResize = resizeOnDpiChanged(platformWindow->window()); - if (doResize) { - platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); - platformWindow->updateFullFrameMargins(); - const auto prcNewWindow = reinterpret_cast<RECT *>(lParam); - qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED" - << platformWindow->window() << *prcNewWindow; - SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, - prcNewWindow->right - prcNewWindow->left, - prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); - platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); - } - platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange); - return doResize; - } + 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(); @@ -1449,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; } @@ -1491,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); } } @@ -1521,7 +1426,7 @@ bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg) } QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos, - QWindowsKeyMapper::queryKeyboardModifiers()); + keyMapper()->queryKeyboardModifiers()); return true; } #endif @@ -1542,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); @@ -1551,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) @@ -1571,12 +1475,6 @@ void QWindowsContext::setAsyncExpose(bool value) d->m_asyncExpose = value; } -QTouchDevice *QWindowsContext::touchDevice() const -{ - return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ? - d->m_pointerHandler.touchDevice() : d->m_mouseHandler.touchDevice(); -} - DWORD QWindowsContext::readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue) { const auto value = @@ -1623,7 +1521,6 @@ static inline bool isTopLevel(HWND hwnd) There is another one for timers, sockets, etc in QEventDispatcherWin32. - \ingroup qt-lighthouse-win */ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -1653,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(); @@ -1672,11 +1569,7 @@ static inline QByteArray nativeEventType() { return QByteArrayLiteral("windows_g bool QWindowsContext::filterNativeEvent(MSG *msg, LRESULT *result) { QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) qintptr filterResult = 0; -#else - long filterResult = 0; -#endif if (dispatcher && dispatcher->filterNativeEvent(nativeEventType(), msg, &filterResult)) { *result = LRESULT(filterResult); return true; @@ -1687,11 +1580,7 @@ bool QWindowsContext::filterNativeEvent(MSG *msg, LRESULT *result) // Send to QWindowSystemInterface bool QWindowsContext::filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) qintptr filterResult = 0; -#else - long filterResult = 0; -#endif if (QWindowSystemInterface::handleNativeEvent(window, nativeEventType(), msg, &filterResult)) { *result = LRESULT(filterResult); return true; diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 8027f09389..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,96 +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 QTouchDevice; - -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 *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); - - // 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; - - // 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 QPointingDevice; class QWindowsContext { Q_DISABLE_COPY_MOVE(QWindowsContext) public: + using HandleBaseWindowHash = QHash<HWND, QWindowsWindow *>; enum SystemInfoFlags { @@ -173,13 +65,16 @@ public: bool initTouch(); bool initTouch(unsigned integrationOptions); // For calls from QWindowsIntegration::QWindowsIntegration() only. - bool initTablet(unsigned integrationOptions); + void registerTouchWindows(); + bool initTablet(); bool initPointer(unsigned integrationOptions); + bool disposeTablet(); bool initPowerNotificationHandler(); int defaultDPI() const; + static QString classNamePrefix(); QString registerWindowClass(const QWindow *w); QString registerWindowClass(QString cname, WNDPROC proc, unsigned style = 0, HBRUSH brush = nullptr, @@ -193,8 +88,6 @@ public: static QWindowsContext *instance(); - static QString windowsErrorMessage(unsigned long errorCode); - void addWindow(HWND, QWindowsWindow *w); void removeWindow(HWND); @@ -221,9 +114,11 @@ public: QSharedPointer<QWindowCreationContext> setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx); QSharedPointer<QWindowCreationContext> windowCreationContext() const; - void setTabletAbsoluteRange(int a); - void setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness); - static int processDpiAwareness(); + static void setTabletAbsoluteRange(int a); + + static bool setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness); + static QtWindows::DpiAwareness processDpiAwareness(); + static QtWindows::DpiAwareness windowDpiAwareness(HWND hwnd); void setDetectAltGrModifier(bool a); @@ -231,21 +126,21 @@ 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); + static void forceNcCalcSize(HWND hwnd); + static bool systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi = 0); static bool systemParametersInfoForScreen(unsigned action, unsigned param, void *out, const QPlatformScreen *screen = nullptr); @@ -259,8 +154,6 @@ public: static DWORD readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue); - QTouchDevice *touchDevice() const; - static bool filterNativeEvent(MSG *msg, LRESULT *result); static bool filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result); diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index 17e8cffb76..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,8 @@ #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> #include <QtCore/qscopedpointer.h> @@ -64,25 +30,23 @@ 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 \brief Cache key for storing values in a QHash with a QCursor as key. \internal - \ingroup qt-lighthouse-win */ QWindowsPixmapCursorCacheKey::QWindowsPixmapCursorCacheKey(const QCursor &c) : bitmapCacheKey(c.pixmap().cacheKey()), maskCacheKey(0) { if (!bitmapCacheKey) { - Q_ASSERT(c.bitmap()); - Q_ASSERT(c.mask()); - bitmapCacheKey = c.bitmap()->cacheKey(); - maskCacheKey = c.mask()->cacheKey(); + Q_ASSERT(!c.bitmap().isNull()); + Q_ASSERT(!c.mask().isNull()); + bitmapCacheKey = c.bitmap().cacheKey(); + maskCacheKey = c.mask().cacheKey(); } } @@ -96,14 +60,13 @@ QWindowsPixmapCursorCacheKey::QWindowsPixmapCursorCacheKey(const QCursor &c) as do the Window manager frames (resize/move handles). \internal - \ingroup qt-lighthouse-win \sa QWindowsWindowCursor */ HCURSOR QWindowsCursor::createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor) { HCURSOR cur = nullptr; - const qreal pixmapScaleFactor = scaleFactor / pixmap.devicePixelRatioF(); + const qreal pixmapScaleFactor = scaleFactor / pixmap.devicePixelRatio(); if (!qFuzzyCompare(pixmapScaleFactor, 1)) { pixmap = pixmap.scaled((pixmapScaleFactor * QSizeF(pixmap.size())).toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation); @@ -142,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) @@ -160,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()); @@ -168,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()); - QImage bbits = cursor.bitmap()->toImage(); - QImage mbits = cursor.mask()->toImage(); - scaleFactor /= bbits.devicePixelRatioF(); + 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); @@ -475,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 @@ -589,7 +559,7 @@ QWindowsCursor::QWindowsCursor(const QPlatformScreen *screen) : m_screen(screen) { static const bool dummy = initResources(); - Q_UNUSED(dummy) + Q_UNUSED(dummy); } inline CursorHandlePtr QWindowsCursor::cursorHandle(const QCursor &cursor) @@ -653,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() @@ -686,6 +661,30 @@ void QWindowsCursor::setPos(const QPoint &pos) SetCursorPos(pos.x() , pos.y()); } +/* + The standard size is 32x32, even though the cursor is actually just + 16 pixels large. If a large cursor is set in the accessibility settings, + then the cursor increases with 8 pixels for each step. +*/ +QSize QWindowsCursor::size() const +{ + const QPair<DWORD,bool> cursorSizeSetting = + QWinRegistryKey(HKEY_CURRENT_USER, LR"(Control Panel\Cursors)") + .dwordValue(L"CursorBaseSize"); + const int baseSize = screenCursorSize(m_screen).width() / 2; + if (!cursorSizeSetting.second) + return QSize(baseSize / 2, baseSize / 2); + + // The registry values are dpi-independent, so we need to scale the result. + int cursorSizeValue = cursorSizeSetting.first * m_screen->logicalDpi().first + / m_screen->logicalBaseDpi().first; + + // map from registry value 32-256 to 0-14, and from there to pixels + cursorSizeValue = (cursorSizeValue - 2 * baseSize) / baseSize; + const int cursorSize = baseSize + cursorSizeValue * (baseSize / 2); + return QSize(cursorSize, cursorSize); +} + QPixmap QWindowsCursor::dragDefaultCursor(Qt::DropAction action) const { switch (action) { @@ -791,7 +790,6 @@ HCURSOR QWindowsCursor::hCursor(const QCursor &c) const cursor handle resource. \internal - \ingroup qt-lighthouse-win \sa QWindowsCursor */ diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h index b896f4c7a9..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 @@ -61,9 +25,9 @@ inline bool operator==(const QWindowsPixmapCursorCacheKey &k1, const QWindowsPix return k1.bitmapCacheKey == k2.bitmapCacheKey && k1.maskCacheKey == k2.maskCacheKey; } -inline uint qHash(const QWindowsPixmapCursorCacheKey &k, uint seed) noexcept +inline size_t qHash(const QWindowsPixmapCursorCacheKey &k, size_t seed) noexcept { - return (uint(k.bitmapCacheKey) + uint(k.maskCacheKey)) ^ seed; + return (size_t(k.bitmapCacheKey) + size_t(k.maskCacheKey)) ^ seed; } class CursorHandle @@ -113,6 +77,8 @@ public: QPoint pos() const override; void setPos(const QPoint &pos) override; + QSize size() const override; + static HCURSOR createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor = 1); static HCURSOR createPixmapCursor(const PixmapCursor &pc, qreal scaleFactor = 1) { return createPixmapCursor(pc.pixmap, pc.hotSpot, scaleFactor); } static PixmapCursor customCursor(Qt::CursorShape cursorShape, const QPlatformScreen *screen = nullptr); diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index cdb4e407d1..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" @@ -55,7 +15,9 @@ #include <QtGui/qcolor.h> #include <QtCore/qdebug.h> -#include <QtCore/qregularexpression.h> +#if QT_CONFIG(regularexpression) +# include <QtCore/qregularexpression.h> +#endif #include <QtCore/qtimer.h> #include <QtCore/qdir.h> #include <QtCore/qscopedpointer.h> @@ -68,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(QLatin1Char('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) @@ -128,7 +64,6 @@ namespace QWindowsDialogs remove all those messages (usually 1) and post the last one with a reset button state. - \ingroup qt-lighthouse-win */ void eatMouseMove() @@ -141,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 /*! @@ -149,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 @@ -171,7 +122,6 @@ void eatMouseMove() \sa QWindowsDialogHelperBase \internal - \ingroup qt-lighthouse-win */ class QWindowsNativeDialogBase : public QObject @@ -211,24 +161,28 @@ private: \sa QWindowsDialogThread, QWindowsNativeDialogBase \internal - \ingroup qt-lighthouse-win */ 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; } } @@ -255,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(); } @@ -265,7 +219,6 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialo \sa QWindowsDialogHelperBase \internal - \ingroup qt-lighthouse-win */ class QWindowsDialogThread : public QThread @@ -275,7 +228,7 @@ public: explicit QWindowsDialogThread(const QWindowsNativeDialogBasePtr &d, HWND owner) : m_dialog(d), m_owner(owner) {} - void run(); + void run() override; private: const QWindowsNativeDialogBasePtr m_dialog; @@ -285,6 +238,7 @@ private: void QWindowsDialogThread::run() { qCDebug(lcQpaDialogs) << '>' << __FUNCTION__; + QComHelper comInit(COINIT_APARTMENTTHREADED); m_dialog->exec(m_owner); qCDebug(lcQpaDialogs) << '<' << __FUNCTION__; } @@ -341,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; } @@ -406,7 +326,6 @@ void QWindowsDialogHelperBase<BaseClass>::exec() does not reliably work. Provides thread-safe setters (for the non-modal case). \internal - \ingroup qt-lighthouse-win \sa QFileDialogOptions */ @@ -500,26 +419,31 @@ inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer<QFile \sa QWindowsNativeFileDialogBase, QWindowsFileDialogHelper \internal - \ingroup qt-lighthouse-win */ class QWindowsNativeFileDialogBase; -class QWindowsNativeFileDialogEventHandler : public QWindowsComBase<IFileDialogEvents> +class QWindowsNativeFileDialogEventHandler : public QComObject<IFileDialogEvents> { Q_DISABLE_COPY_MOVE(QWindowsNativeFileDialogEventHandler) public: static IFileDialogEvents *create(QWindowsNativeFileDialogBase *nativeFileDialog); // IFileDialogEvents methods - IFACEMETHODIMP OnFileOk(IFileDialog *); - IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; } - IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *); - IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; } - IFACEMETHODIMP OnSelectionChange(IFileDialog *); - IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; } - IFACEMETHODIMP OnTypeChange(IFileDialog *); - IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; } + IFACEMETHODIMP OnFileOk(IFileDialog *) override; + IFACEMETHODIMP OnFolderChange(IFileDialog *) override { return S_OK; } + IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *) override; + IFACEMETHODIMP OnSelectionChange(IFileDialog *) override; + IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, + FDE_SHAREVIOLATION_RESPONSE *) override + { + return S_OK; + } + IFACEMETHODIMP OnTypeChange(IFileDialog *) override; + IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) override + { + return S_OK; + } QWindowsNativeFileDialogEventHandler(QWindowsNativeFileDialogBase *nativeFileDialog) : m_nativeFileDialog(nativeFileDialog) {} @@ -546,7 +470,6 @@ IFileDialogEvents *QWindowsNativeFileDialogEventHandler::create(QWindowsNativeFi \sa QWindowsNativeFileDialogBase \internal - \ingroup qt-lighthouse-win */ class QWindowsShellItem { @@ -593,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 @@ -633,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); } @@ -667,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 }; @@ -690,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; @@ -789,7 +722,6 @@ QDebug operator<<(QDebug d, IShellItem *i) \sa QWindowsNativeFileDialogEventHandler, QWindowsFileDialogHelper \internal - \ingroup qt-lighthouse-win */ class QWindowsNativeFileDialogBase : public QWindowsNativeDialogBase @@ -915,7 +847,7 @@ IShellItem *QWindowsNativeFileDialogBase::shellItem(const QUrl &url) return nullptr; } return result; - } else if (url.scheme() == QLatin1String("clsid")) { + } else if (url.scheme() == u"clsid") { // Support for virtual folders via GUID // (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx) // specified as "clsid:<GUID>" (without '{', '}'). @@ -1034,26 +966,33 @@ static QList<FilterSpec> filterSpecs(const QStringList &filters, result.reserve(filters.size()); *totalStringLength = 0; +#if QT_CONFIG(regularexpression) const QRegularExpression filterSeparatorRE(QStringLiteral("[;\\s]+")); const QString separator = QStringLiteral(";"); Q_ASSERT(filterSeparatorRE.isValid()); +#endif + // Split filter specification as 'Texts (*.txt[;] *.doc)', '*.txt[;] *.doc' // into description and filters specification as '*.txt;*.doc' for (const QString &filterString : filters) { - const int openingParenPos = filterString.lastIndexOf(QLatin1Char('(')); + const int openingParenPos = filterString.lastIndexOf(u'('); const int closingParenPos = openingParenPos != -1 ? - filterString.indexOf(QLatin1Char(')'), openingParenPos + 1) : -1; + filterString.indexOf(u')', openingParenPos + 1) : -1; FilterSpec filterSpec; filterSpec.filter = closingParenPos == -1 ? filterString : filterString.mid(openingParenPos + 1, closingParenPos - openingParenPos - 1).trimmed(); if (filterSpec.filter.isEmpty()) - filterSpec.filter += QLatin1Char('*'); + filterSpec.filter += u'*'; +#if QT_CONFIG(regularexpression) filterSpec.filter.replace(filterSeparatorRE, separator); +#else + filterSpec.filter.replace(u' ', u';'); +#endif filterSpec.description = filterString; if (hideFilterDetails && openingParenPos != -1) { // Do not show pattern in description filterSpec.description.truncate(openingParenPos); - while (filterSpec.description.endsWith(QLatin1Char(' '))) + while (filterSpec.description.endsWith(u' ')) filterSpec.description.truncate(filterSpec.description.size() - 1); } *totalStringLength += filterSpec.filter.size() + filterSpec.description.size(); @@ -1084,10 +1023,13 @@ void QWindowsNativeFileDialogBase::setNameFilters(const QStringList &filters) // 'AAA files (a.*) (a.*)' QString description = specs[i].description; const QString &filter = specs[i].filter; - if (!m_hideFiltersDetails && !filter.startsWith(QLatin1String("*."))) { - const int pos = description.lastIndexOf(QLatin1Char('(')); - if (pos > 0) + if (!m_hideFiltersDetails && !filter.startsWith(u"*.")) { + const int pos = description.lastIndexOf(u'('); + if (pos > 0) { description.truncate(pos); + while (!description.isEmpty() && description.back().isSpace()) + description.chop(1); + } } // Add to buffer. comFilterSpec[i].pszName = ptr; @@ -1151,8 +1093,8 @@ static bool isHexRange(const QString& s, int start, int end) for (;start < end; ++start) { QChar ch = s.at(start); if (!(ch.isDigit() - || (ch >= QLatin1Char('a') && ch <= QLatin1Char('f')) - || (ch >= QLatin1Char('A') && ch <= QLatin1Char('F')))) + || (ch >= u'a' && ch <= u'f') + || (ch >= u'A' && ch <= u'F'))) return false; } return true; @@ -1161,7 +1103,7 @@ static bool isHexRange(const QString& s, int start, int end) static inline bool isClsid(const QString &s) { // detect "374DE290-123F-4565-9164-39C4925E467B". - const QChar dash(QLatin1Char('-')); + const QChar dash(u'-'); return s.size() == 36 && isHexRange(s, 0, 8) && s.at(8) == dash @@ -1204,7 +1146,7 @@ void QWindowsNativeFileDialogBase::selectNameFilter(const QString &filter) if (index < 0) { qWarning("%s: Invalid parameter '%s' not found in '%s'.", __FUNCTION__, qPrintable(filter), - qPrintable(m_nameFilters.join(QLatin1String(", ")))); + qPrintable(m_nameFilters.join(u", "))); return; } m_fileDialog->SetFileTypeIndex(index + 1); // one-based. @@ -1259,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); @@ -1295,7 +1237,6 @@ HRESULT QWindowsNativeFileDialogEventHandler::OnFileOk(IFileDialog *) Implements single-selection methods. \internal - \ingroup qt-lighthouse-win */ class QWindowsNativeSaveFileDialog : public QWindowsNativeFileDialogBase @@ -1313,15 +1254,15 @@ public: // Also handles the simple name filter case "*.txt" -> "txt" static inline QString suffixFromFilter(const QString &filter) { - int suffixPos = filter.indexOf(QLatin1String("*.")); + int suffixPos = filter.indexOf(u"*."); if (suffixPos < 0) return QString(); suffixPos += 2; - int endPos = filter.indexOf(QLatin1Char(' '), suffixPos + 1); + int endPos = filter.indexOf(u' ', suffixPos + 1); if (endPos < 0) - endPos = filter.indexOf(QLatin1Char(';'), suffixPos + 1); + endPos = filter.indexOf(u';', suffixPos + 1); if (endPos < 0) - endPos = filter.indexOf(QLatin1Char(')'), suffixPos + 1); + endPos = filter.indexOf(u')', suffixPos + 1); if (endPos < 0) endPos = filter.size(); return filter.mid(suffixPos, endPos - suffixPos); @@ -1372,7 +1313,6 @@ QList<QUrl> QWindowsNativeSaveFileDialog::selectedFiles() const Implements multi-selection methods. \internal - \ingroup qt-lighthouse-win */ class QWindowsNativeOpenFileDialog : public QWindowsNativeFileDialogBase @@ -1397,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); } @@ -1406,41 +1346,41 @@ static void cleanupTemporaryItemCopies() static bool validFileNameCharacter(QChar c) { - return c.isLetterOrNumber() || c == QLatin1Char('_') || c == QLatin1Char('-'); + return c.isLetterOrNumber() || c == u'_' || c == u'-'; } QString tempFilePattern(QString name) { - const int lastSlash = qMax(name.lastIndexOf(QLatin1Char('/')), - name.lastIndexOf(QLatin1Char('\\'))); + const int lastSlash = qMax(name.lastIndexOf(u'/'), + name.lastIndexOf(u'\\')); if (lastSlash != -1) name.remove(0, lastSlash + 1); - int lastDot = name.lastIndexOf(QLatin1Char('.')); + 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))) - name[i] = QLatin1Char('_'); + name[i] = u'_'; } - name.prepend(QDir::tempPath() + QLatin1Char('/')); + name.prepend(QDir::tempPath() + u'/'); return 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(); } @@ -1456,7 +1396,7 @@ static QString createTemporaryItemCopy(QWindowsShellItem &qItem, QString *errorM static QUrl itemToDialogUrl(QWindowsShellItem &qItem, QString *errorMessage) { QUrl url = qItem.url(); - if (url.isLocalFile() || url.scheme().startsWith(QLatin1String("http"))) + if (url.isLocalFile() || url.scheme().startsWith(u"http")) return url; const QString path = qItem.path(); if (path.isEmpty() && !qItem.isDir() && qItem.canStream()) { @@ -1545,7 +1485,6 @@ QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOp but only on QQuickWindows, which do not have a fallback. \internal - \ingroup qt-lighthouse-win */ class QWindowsFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper> @@ -1612,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 @@ -1646,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 @@ -1681,7 +1620,6 @@ QString QWindowsFileDialogHelper::selectedNameFilter() const \internal \sa QWindowsXpFileDialogHelper - \ingroup qt-lighthouse-win */ class QWindowsXpNativeFileDialog : public QWindowsNativeDialogBase @@ -1701,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); @@ -1713,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, @@ -1774,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) { @@ -1795,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); } @@ -1817,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]) @@ -1859,10 +1770,12 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow // for the target. If it contains any invalid character, the dialog // will not show. ofn->nMaxFile = 65535; - const QString initiallySelectedFile = - QDir::toNativeSeparators(m_data.selectedFile()).remove(QLatin1Char('<')). - remove(QLatin1Char('>')).remove(QLatin1Char('"')).remove(QLatin1Char('|')); - ofn->lpstrFile = qStringToWCharArray(initiallySelectedFile, ofn->nMaxFile); + QString initiallySelectedFile = m_data.selectedFile(); + initiallySelectedFile.remove(u'<'); + initiallySelectedFile.remove(u'>'); + initiallySelectedFile.remove(u'"'); + initiallySelectedFile.remove(u'|'); + ofn->lpstrFile = qStringToWCharArray(QDir::toNativeSeparators(initiallySelectedFile), ofn->nMaxFile); ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_data.directory().toLocalFile())); ofn->lpstrTitle = (wchar_t*)m_title.utf16(); // Determine lpstrDefExt. Note that the current MSDN docs document this @@ -1872,7 +1785,7 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow // the extension of the current filter". if (m_options->acceptMode() == QFileDialogOptions::AcceptSave) { QString defaultSuffix = m_options->defaultSuffix(); - if (defaultSuffix.startsWith(QLatin1Char('.'))) + if (defaultSuffix.startsWith(u'.')) defaultSuffix.remove(0, 1); // QTBUG-33156, also create empty strings to trigger the appending mechanism. ofn->lpstrDefExt = qStringToWCharArray(defaultSuffix); @@ -1895,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)); @@ -1905,7 +1818,7 @@ QList<QUrl> QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedF wchar_t *ptr = ofn.lpstrFile + dir.size() + 1; if (*ptr) { result.pop_front(); - const QString path = dir + QLatin1Char('/'); + const QString path = dir + u'/'; while (*ptr) { const QString fileName = QString::fromWCharArray(ptr); result.push_back(QUrl::fromLocalFile(path + fileName)); @@ -1927,7 +1840,6 @@ QList<QUrl> QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedF \sa QWindowsXpNativeFileDialog \internal - \ingroup qt-lighthouse-win */ class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper> @@ -2004,7 +1916,6 @@ QString QWindowsXpFileDialogHelper::selectedNameFilter() const \sa QWindowsColorDialogHelper \sa #define USE_NATIVE_COLOR_DIALOG \internal - \ingroup qt-lighthouse-win */ using SharedPointerColor = QSharedPointer<QColor>; @@ -2039,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); @@ -2053,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) @@ -2084,7 +1984,6 @@ void QWindowsNativeColorDialog::doExec(HWND owner) \sa #define USE_NATIVE_COLOR_DIALOG \sa QWindowsNativeColorDialog \internal - \ingroup qt-lighthouse-win */ class QWindowsColorDialogHelper : public QWindowsDialogHelperBase<QPlatformColorDialogHelper> diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.h b/src/plugins/platforms/windows/qwindowsdialoghelpers.h index 8686749011..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, @@ -80,7 +44,7 @@ public: protected: QWindowsDialogHelperBase() = default; QWindowsNativeDialogBase *nativeDialog() const; - inline bool hasNativeDialog() const { return m_nativeDialog; } + inline bool hasNativeDialog() const { return !m_nativeDialog.isNull(); } void timerEvent(QTimerEvent *) override; private: diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 3e4c93d47a..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> @@ -75,7 +41,6 @@ QT_BEGIN_NAMESPACE \sa QWindowsOleDropSource \internal - \ingroup qt-lighthouse-win */ class QWindowsDragCursorWindow : public QRasterWindow @@ -135,7 +100,6 @@ void QWindowsDragCursorWindow::setPixmap(const QPixmap &p) \sa QWindowsDrag \internal - \ingroup qt-lighthouse-win */ IDataObject *QWindowsDropMimeData::retrieveDataObject() const @@ -192,20 +156,6 @@ static inline Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState) return modifiers; } -static inline Qt::MouseButtons toQtMouseButtons(DWORD keyState) -{ - Qt::MouseButtons buttons = Qt::NoButton; - - if (keyState & MK_LBUTTON) - buttons |= Qt::LeftButton; - if (keyState & MK_RBUTTON) - buttons |= Qt::RightButton; - if (keyState & MK_MBUTTON) - buttons |= Qt::MidButton; - - return buttons; -} - static Qt::KeyboardModifiers lastModifiers = Qt::NoModifier; static Qt::MouseButtons lastButtons = Qt::NoButton; @@ -217,10 +167,9 @@ static Qt::MouseButtons lastButtons = Qt::NoButton; \sa QWindowsDrag \internal - \ingroup qt-lighthouse-win */ -class QWindowsOleDropSource : public QWindowsComBase<IDropSource> +class QWindowsOleDropSource : public QComObject<IDropSource> { public: enum Mode { @@ -234,8 +183,8 @@ public: void createCursors(); // IDropSource methods - STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState); - STDMETHOD(GiveFeedback)(DWORD dwEffect); + STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState) override; + STDMETHOD(GiveFeedback)(DWORD dwEffect) override; private: struct CursorEntry { @@ -389,7 +338,10 @@ void QWindowsOleDropSource::createCursors() QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) { - Qt::MouseButtons buttons = toQtMouseButtons(grfKeyState); + // In some rare cases, when a mouse button is released but the mouse is static, + // grfKeyState will not be updated with these released buttons until the mouse + // is moved. So we use the async key state given by queryMouseButtons() instead. + Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons(); SCODE result = S_OK; if (fEscapePressed || QWindowsDrag::isCanceled()) { @@ -398,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; } } @@ -434,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 @@ -485,7 +437,6 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) \sa QWindowsDrag \internal - \ingroup qt-lighthouse-win */ QWindowsOleDropTarget::QWindowsOleDropTarget(QWindow *w) : m_window(w) @@ -509,7 +460,7 @@ void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState, const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect); lastModifiers = toQtKeyboardModifiers(grfKeyState); - lastButtons = toQtMouseButtons(grfKeyState); + lastButtons = QWindowsMouseHandler::queryMouseButtons(); const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(), @@ -577,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, @@ -607,7 +559,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, QWindowsDrag *windowsDrag = QWindowsDrag::instance(); lastModifiers = toQtKeyboardModifiers(grfKeyState); - lastButtons = toQtMouseButtons(grfKeyState); + lastButtons = QWindowsMouseHandler::queryMouseButtons(); const QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(), @@ -659,11 +611,9 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, \class QWindowsDrag \brief Windows drag implementation. \internal - \ingroup qt-lighthouse-win */ bool QWindowsDrag::m_canceled = false; -bool QWindowsDrag::m_dragging = false; QWindowsDrag::QWindowsDrag() = default; @@ -696,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? @@ -711,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 5f30c59882..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,17 +23,19 @@ public: IDataObject *retrieveDataObject() const override; }; -class QWindowsOleDropTarget : public QWindowsComBase<IDropTarget> +class QWindowsOleDropTarget : public QComObject<IDropTarget> { public: explicit QWindowsOleDropTarget(QWindow *w); ~QWindowsOleDropTarget() override; // IDropTarget methods - STDMETHOD(DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); - STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); - STDMETHOD(DragLeave)(); - STDMETHOD(Drop)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); + STDMETHOD(DragEnter) + (LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) override; + STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) override; + STDMETHOD(DragLeave)() override; + STDMETHOD(Drop) + (LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) override; private: void handleDrag(QWindow *window, DWORD grfKeyState, const QPoint &, LPDWORD pdwEffect); @@ -92,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; } @@ -103,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 c9dd1c7c17..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. @@ -54,7 +20,6 @@ QT_BEGIN_NAMESPACE (instead of creating local hyperlinks). \internal - \ingroup qt-lighthouse-win */ QWindowsDropDataObject::QWindowsDropDataObject(QMimeData *mimeData) : @@ -90,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 16ba7b036a..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 @@ -51,8 +15,8 @@ public: ~QWindowsDropDataObject() override; // overridden IDataObject methods - STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium); - STDMETHOD(QueryGetData)(LPFORMATETC pformatetc); + STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium) override; + STDMETHOD(QueryGetData)(LPFORMATETC pformatetc) override; private: bool shouldIgnore(LPFORMATETC pformatetc) const; diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp deleted file mode 100644 index e9f3dc5189..0000000000 --- a/src/plugins/platforms/windows/qwindowseglcontext.cpp +++ /dev/null @@ -1,920 +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$ -** -****************************************************************************/ - -#include "qwindowseglcontext.h" -#include "qwindowscontext.h" -#include "qwindowswindow.h" - -#include <QtCore/qdebug.h> -#include <QtGui/qopenglcontext.h> - -#if defined(QT_OPENGL_ES_2_ANGLE) || defined(QT_OPENGL_DYNAMIC) -# include <EGL/eglext.h> -#endif - -QT_BEGIN_NAMESPACE - -/*! - \class QWindowsEGLStaticContext - \brief Static data for QWindowsEGLContext. - - Keeps the display. The class is shared via QSharedPointer in the windows, the - contexts and in QWindowsIntegration. The display will be closed if the last instance - is deleted. - - No EGL or OpenGL functions are called directly. Instead, they are resolved - dynamically. This works even if the plugin links directly to libegl/libglesv2 so - there is no need to differentiate between dynamic or Angle-only builds in here. - - \internal - \ingroup qt-lighthouse-win -*/ - -QWindowsLibEGL QWindowsEGLStaticContext::libEGL; -QWindowsLibGLESv2 QWindowsEGLStaticContext::libGLESv2; - -#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) - -#ifdef Q_CC_MINGW -static void *resolveFunc(HMODULE lib, const char *name) -{ - QString baseNameStr = QString::fromLatin1(name); - QString nameStr; - void *proc = 0; - - // Play nice with 32-bit mingw: Try func first, then func@0, func@4, - // func@8, func@12, ..., func@64. The def file does not provide any aliases - // in libEGL and libGLESv2 in these builds which results in exporting - // function names like eglInitialize@12. This cannot be fixed without - // breaking binary compatibility. So be flexible here instead. - - int argSize = -1; - while (!proc && argSize <= 64) { - nameStr = baseNameStr; - if (argSize >= 0) - nameStr += QLatin1Char('@') + QString::number(argSize); - argSize = argSize < 0 ? 0 : argSize + 4; - proc = (void *) ::GetProcAddress(lib, nameStr.toLatin1().constData()); - } - return proc; -} -#else -static inline void *resolveFunc(HMODULE lib, const char *name) -{ - return ::GetProcAddress(lib, name); -} -#endif // Q_CC_MINGW - -void *QWindowsLibEGL::resolve(const char *name) -{ - return m_lib ? resolveFunc(m_lib, name) : nullptr; -} - -#endif // !QT_STATIC - -#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) -# define RESOLVE(signature, name) signature(resolve( #name )); -#else -# define RESOLVE(signature, name) signature(&::name); -#endif - -bool QWindowsLibEGL::init() -{ - const char dllName[] = QT_STRINGIFY(LIBEGL_NAME) -#if defined(QT_DEBUG) - "d" -#endif - ""; - - qCDebug(lcQpaGl) << "Qt: Using EGL from" << dllName; - -#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) - m_lib = ::LoadLibraryW((const wchar_t *) QString::fromLatin1(dllName).utf16()); - if (!m_lib) { - qErrnoWarning(::GetLastError(), "Failed to load %s", dllName); - return false; - } -#endif - - eglGetError = RESOLVE((EGLint (EGLAPIENTRY *)(void)), eglGetError); - eglGetDisplay = RESOLVE((EGLDisplay (EGLAPIENTRY *)(EGLNativeDisplayType)), eglGetDisplay); - eglInitialize = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay, EGLint *, EGLint *)), eglInitialize); - eglTerminate = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay)), eglTerminate); - eglChooseConfig = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)), eglChooseConfig); - eglGetConfigAttrib = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay, EGLConfig, EGLint, EGLint *)), eglGetConfigAttrib); - eglCreateWindowSurface = RESOLVE((EGLSurface (EGLAPIENTRY *)(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *)), eglCreateWindowSurface); - eglCreatePbufferSurface = RESOLVE((EGLSurface (EGLAPIENTRY *)(EGLDisplay , EGLConfig, const EGLint *)), eglCreatePbufferSurface); - eglDestroySurface = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLSurface )), eglDestroySurface); - eglBindAPI = RESOLVE((EGLBoolean (EGLAPIENTRY * )(EGLenum )), eglBindAPI); - eglSwapInterval = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLint )), eglSwapInterval); - eglCreateContext = RESOLVE((EGLContext (EGLAPIENTRY *)(EGLDisplay , EGLConfig , EGLContext , const EGLint *)), eglCreateContext); - eglDestroyContext = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay, EGLContext)), eglDestroyContext); - eglMakeCurrent = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLSurface , EGLSurface , EGLContext )), eglMakeCurrent); - eglGetCurrentContext = RESOLVE((EGLContext (EGLAPIENTRY *)(void)), eglGetCurrentContext); - eglGetCurrentSurface = RESOLVE((EGLSurface (EGLAPIENTRY *)(EGLint )), eglGetCurrentSurface); - eglGetCurrentDisplay = RESOLVE((EGLDisplay (EGLAPIENTRY *)(void)), eglGetCurrentDisplay); - eglSwapBuffers = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLSurface)), eglSwapBuffers); - eglGetProcAddress = RESOLVE((QFunctionPointer (EGLAPIENTRY * )(const char *)), eglGetProcAddress); - - if (!eglGetError || !eglGetDisplay || !eglInitialize || !eglGetProcAddress) - return false; - - eglGetPlatformDisplayEXT = nullptr; -#ifdef EGL_ANGLE_platform_angle - eglGetPlatformDisplayEXT = reinterpret_cast<EGLDisplay (EGLAPIENTRY *)(EGLenum, void *, const EGLint *)>(eglGetProcAddress("eglGetPlatformDisplayEXT")); -#endif - - return true; -} - -#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) -void *QWindowsLibGLESv2::resolve(const char *name) -{ - return m_lib ? resolveFunc(m_lib, name) : nullptr; -} -#endif // !QT_STATIC - -bool QWindowsLibGLESv2::init() -{ - - const char dllName[] = QT_STRINGIFY(LIBGLESV2_NAME) -#if defined(QT_DEBUG) - "d" -#endif - ""; - - qCDebug(lcQpaGl) << "Qt: Using OpenGL ES 2.0 from" << dllName; -#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) - m_lib = ::LoadLibraryW(reinterpret_cast<LPCWSTR>(QString::fromLatin1(dllName).utf16())); - if (!m_lib) { - qErrnoWarning(int(GetLastError()), "Failed to load %s", dllName); - return false; - } -#endif // !QT_STATIC - - void (APIENTRY * glBindTexture)(GLenum target, GLuint texture) = RESOLVE((void (APIENTRY *)(GLenum , GLuint )), glBindTexture); - GLuint (APIENTRY * glCreateShader)(GLenum type) = RESOLVE((GLuint (APIENTRY *)(GLenum )), glCreateShader); - void (APIENTRY * glClearDepthf)(GLclampf depth) = RESOLVE((void (APIENTRY *)(GLclampf )), glClearDepthf); - glGetString = RESOLVE((const GLubyte * (APIENTRY *)(GLenum )), glGetString); - - return glBindTexture && glCreateShader && glClearDepthf; -} - -QWindowsEGLStaticContext::QWindowsEGLStaticContext(EGLDisplay display) - : m_display(display) -{ -} - -bool QWindowsEGLStaticContext::initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, - EGLDisplay *display, EGLint *major, EGLint *minor) -{ -#ifdef EGL_ANGLE_platform_angle - if (libEGL.eglGetPlatformDisplayEXT - && (preferredType & QWindowsOpenGLTester::AngleBackendMask)) { - const EGLint anglePlatformAttributes[][5] = { - { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_NONE }, - { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, EGL_NONE }, - { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, - EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE, EGL_NONE } - }; - const EGLint *attributes = nullptr; - if (preferredType & QWindowsOpenGLTester::AngleRendererD3d11) - attributes = anglePlatformAttributes[0]; - else if (preferredType & QWindowsOpenGLTester::AngleRendererD3d9) - attributes = anglePlatformAttributes[1]; - else if (preferredType & QWindowsOpenGLTester::AngleRendererD3d11Warp) - attributes = anglePlatformAttributes[2]; - if (attributes) { - *display = libEGL.eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc, attributes); - if (!libEGL.eglInitialize(*display, major, minor)) { - libEGL.eglTerminate(*display); - *display = EGL_NO_DISPLAY; - *major = *minor = 0; - return false; - } - } - } -#else // EGL_ANGLE_platform_angle - Q_UNUSED(preferredType); - Q_UNUSED(dc); - Q_UNUSED(display); - Q_UNUSED(major); - Q_UNUSED(minor); -#endif - return true; -} - -QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester::Renderers preferredType) -{ - const HDC dc = QWindowsContext::instance()->displayContext(); - if (!dc){ - qWarning("%s: No Display", __FUNCTION__); - return nullptr; - } - - if (!libEGL.init()) { - qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); - return nullptr; - } - if (!libGLESv2.init()) { - qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); - return nullptr; - } - - EGLDisplay display = EGL_NO_DISPLAY; - EGLint major = 0; - EGLint minor = 0; - - if (!initializeAngle(preferredType, dc, &display, &major, &minor) - && (preferredType & QWindowsOpenGLTester::AngleRendererD3d11)) { - preferredType &= ~QWindowsOpenGLTester::AngleRendererD3d11; - initializeAngle(preferredType, dc, &display, &major, &minor); - } - - if (display == EGL_NO_DISPLAY) - display = libEGL.eglGetDisplay(dc); - if (!display) { - qWarning("%s: Could not obtain EGL display", __FUNCTION__); - return nullptr; - } - - if (!major && !libEGL.eglInitialize(display, &major, &minor)) { - int err = libEGL.eglGetError(); - qWarning("%s: Could not initialize EGL display: error 0x%x", __FUNCTION__, err); - if (err == 0x3001) - qWarning("%s: When using ANGLE, check if d3dcompiler_4x.dll is available", __FUNCTION__); - return nullptr; - } - - qCDebug(lcQpaGl) << __FUNCTION__ << "Created EGL display" << display << 'v' <<major << '.' << minor; - return new QWindowsEGLStaticContext(display); -} - -QWindowsEGLStaticContext::~QWindowsEGLStaticContext() -{ - qCDebug(lcQpaGl) << __FUNCTION__ << "Releasing EGL display " << m_display; - libEGL.eglTerminate(m_display); -} - -QWindowsOpenGLContext *QWindowsEGLStaticContext::createContext(QOpenGLContext *context) -{ - return new QWindowsEGLContext(this, context->format(), context->shareHandle()); -} - -void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *nativeConfig, int *err) -{ - *err = 0; - EGLSurface surface = libEGL.eglCreateWindowSurface(m_display, nativeConfig, - static_cast<EGLNativeWindowType>(nativeWindow), nullptr); - if (surface == EGL_NO_SURFACE) { - *err = libEGL.eglGetError(); - qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err); - } - - return surface; -} - -void QWindowsEGLStaticContext::destroyWindowSurface(void *nativeSurface) -{ - libEGL.eglDestroySurface(m_display, nativeSurface); -} - -QSurfaceFormat QWindowsEGLStaticContext::formatFromConfig(EGLDisplay display, EGLConfig config, - const QSurfaceFormat &referenceFormat) -{ - QSurfaceFormat format; - EGLint redSize = 0; - EGLint greenSize = 0; - EGLint blueSize = 0; - EGLint alphaSize = 0; - EGLint depthSize = 0; - EGLint stencilSize = 0; - EGLint sampleCount = 0; - - libEGL.eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize); - libEGL.eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize); - libEGL.eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize); - libEGL.eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaSize); - libEGL.eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &depthSize); - libEGL.eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &stencilSize); - libEGL.eglGetConfigAttrib(display, config, EGL_SAMPLES, &sampleCount); - - format.setRenderableType(QSurfaceFormat::OpenGLES); - format.setVersion(referenceFormat.majorVersion(), referenceFormat.minorVersion()); - format.setProfile(referenceFormat.profile()); - format.setOptions(referenceFormat.options()); - - format.setRedBufferSize(redSize); - format.setGreenBufferSize(greenSize); - format.setBlueBufferSize(blueSize); - format.setAlphaBufferSize(alphaSize); - format.setDepthBufferSize(depthSize); - format.setStencilBufferSize(stencilSize); - format.setSamples(sampleCount); - format.setStereo(false); - format.setSwapInterval(referenceFormat.swapInterval()); - - // Clear the EGL error state because some of the above may - // have errored out because the attribute is not applicable - // to the surface type. Such errors don't matter. - libEGL.eglGetError(); - - return format; -} - -/*! - \class QWindowsEGLContext - \brief Open EGL context. - - \section1 Using QWindowsEGLContext for Desktop with ANGLE - \section2 Build Instructions - \list - \o Install the Direct X SDK - \o Checkout and build ANGLE (SVN repository) as explained here: - \l{https://chromium.googlesource.com/angle/angle/+/master/README.md} - When building for 64bit, de-activate the "WarnAsError" option - in every project file (as otherwise integer conversion - warnings will break the build). - \o Run configure.exe with the options "-opengl es2". - \o Build qtbase and test some examples. - \endlist - - \internal - \ingroup qt-lighthouse-win -*/ - -QWindowsEGLContext::QWindowsEGLContext(QWindowsEGLStaticContext *staticContext, - const QSurfaceFormat &format, - QPlatformOpenGLContext *share) - : m_staticContext(staticContext) - , m_eglDisplay(staticContext->display()) -{ - if (!m_staticContext) - return; - - m_eglConfig = chooseConfig(format); - m_format = m_staticContext->formatFromConfig(m_eglDisplay, m_eglConfig, format); - m_shareContext = share ? static_cast<QWindowsEGLContext *>(share)->m_eglContext : nullptr; - - QVector<EGLint> contextAttrs; - const int major = m_format.majorVersion(); - const int minor = m_format.minorVersion(); - if (major > 3 || (major == 3 && minor > 0)) - qWarning("QWindowsEGLContext: ANGLE only partially supports OpenGL ES > 3.0"); - contextAttrs.append(EGL_CONTEXT_MAJOR_VERSION); - contextAttrs.append(major); - contextAttrs.append(EGL_CONTEXT_MINOR_VERSION); - contextAttrs.append(minor); - contextAttrs.append(EGL_NONE); - - QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); - m_eglContext = QWindowsEGLStaticContext::libEGL.eglCreateContext(m_eglDisplay, m_eglConfig, m_shareContext, contextAttrs.constData()); - if (m_eglContext == EGL_NO_CONTEXT && m_shareContext != EGL_NO_CONTEXT) { - m_shareContext = nullptr; - m_eglContext = QWindowsEGLStaticContext::libEGL.eglCreateContext(m_eglDisplay, m_eglConfig, nullptr, contextAttrs.constData()); - } - - if (m_eglContext == EGL_NO_CONTEXT) { - int err = QWindowsEGLStaticContext::libEGL.eglGetError(); - qWarning("QWindowsEGLContext: Failed to create context, eglError: %x, this: %p", err, this); - // ANGLE gives bad alloc when it fails to reset a previously lost D3D device. - // A common cause for this is disabling the graphics adapter used by the app. - if (err == EGL_BAD_ALLOC) - qWarning("QWindowsEGLContext: Graphics device lost. (Did the adapter get disabled?)"); - return; - } - - // Make the context current to ensure the GL version query works. This needs a surface too. - const EGLint pbufferAttributes[] = { - EGL_WIDTH, 1, - EGL_HEIGHT, 1, - EGL_LARGEST_PBUFFER, EGL_FALSE, - EGL_NONE - }; - EGLSurface pbuffer = QWindowsEGLStaticContext::libEGL.eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufferAttributes); - if (pbuffer == EGL_NO_SURFACE) - return; - - EGLDisplay prevDisplay = QWindowsEGLStaticContext::libEGL.eglGetCurrentDisplay(); - if (prevDisplay == EGL_NO_DISPLAY) // when no context is current - prevDisplay = m_eglDisplay; - EGLContext prevContext = QWindowsEGLStaticContext::libEGL.eglGetCurrentContext(); - EGLSurface prevSurfaceDraw = QWindowsEGLStaticContext::libEGL.eglGetCurrentSurface(EGL_DRAW); - EGLSurface prevSurfaceRead = QWindowsEGLStaticContext::libEGL.eglGetCurrentSurface(EGL_READ); - - if (QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, pbuffer, pbuffer, m_eglContext)) { - const GLubyte *s = QWindowsEGLStaticContext::libGLESv2.glGetString(GL_VERSION); - if (s) { - QByteArray version = QByteArray(reinterpret_cast<const char *>(s)); - int major, minor; - if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor)) { - m_format.setMajorVersion(major); - m_format.setMinorVersion(minor); - } - } - m_format.setProfile(QSurfaceFormat::NoProfile); - m_format.setOptions(QSurfaceFormat::FormatOptions()); - QWindowsEGLStaticContext::libEGL.eglMakeCurrent(prevDisplay, prevSurfaceDraw, prevSurfaceRead, prevContext); - } - QWindowsEGLStaticContext::libEGL.eglDestroySurface(m_eglDisplay, pbuffer); -} - -QWindowsEGLContext::~QWindowsEGLContext() -{ - if (m_eglContext != EGL_NO_CONTEXT) { - QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext); - m_eglContext = EGL_NO_CONTEXT; - } -} - -bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface) -{ - Q_ASSERT(surface->surface()->supportsOpenGL()); - - QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); - - auto *window = static_cast<QWindowsWindow *>(surface); - window->aboutToMakeCurrent(); - int err = 0; - auto eglSurface = static_cast<EGLSurface>(window->surface(m_eglConfig, &err)); - if (eglSurface == EGL_NO_SURFACE) { - if (err == EGL_CONTEXT_LOST) { - m_eglContext = EGL_NO_CONTEXT; - qCDebug(lcQpaGl) << "Got EGL context lost in createWindowSurface() for context" << this; - } else if (err == EGL_BAD_ACCESS) { - // With ANGLE this means no (D3D) device and can happen when disabling/changing graphics adapters. - qCDebug(lcQpaGl) << "Bad access (missing device?) in createWindowSurface() for context" << this; - // Simulate context loss as the context is useless. - QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext); - m_eglContext = EGL_NO_CONTEXT; - } - return false; - } - - // shortcut: on some GPUs, eglMakeCurrent is not a cheap operation - if (QWindowsEGLStaticContext::libEGL.eglGetCurrentContext() == m_eglContext && - QWindowsEGLStaticContext::libEGL.eglGetCurrentDisplay() == m_eglDisplay && - QWindowsEGLStaticContext::libEGL.eglGetCurrentSurface(EGL_READ) == eglSurface && - QWindowsEGLStaticContext::libEGL.eglGetCurrentSurface(EGL_DRAW) == eglSurface) { - return true; - } - - const bool ok = QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_eglContext); - if (ok) { - const int requestedSwapInterval = surface->format().swapInterval(); - if (requestedSwapInterval >= 0 && m_swapInterval != requestedSwapInterval) { - m_swapInterval = requestedSwapInterval; - QWindowsEGLStaticContext::libEGL.eglSwapInterval(m_staticContext->display(), m_swapInterval); - } - } else { - err = QWindowsEGLStaticContext::libEGL.eglGetError(); - // EGL_CONTEXT_LOST (loss of the D3D device) is not necessarily fatal. - // Qt Quick is able to recover for example. - if (err == EGL_CONTEXT_LOST) { - m_eglContext = EGL_NO_CONTEXT; - qCDebug(lcQpaGl) << "Got EGL context lost in makeCurrent() for context" << this; - // Drop the surface. Will recreate on the next makeCurrent. - window->invalidateSurface(); - } else { - qWarning("%s: Failed to make surface current. eglError: %x, this: %p", __FUNCTION__, err, this); - } - } - - return ok; -} - -void QWindowsEGLContext::doneCurrent() -{ - QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); - bool ok = QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (!ok) - qWarning("%s: Failed to make no context/surface current. eglError: %d, this: %p", __FUNCTION__, - QWindowsEGLStaticContext::libEGL.eglGetError(), this); -} - -void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface) -{ - QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); - auto *window = static_cast<QWindowsWindow *>(surface); - int err = 0; - auto eglSurface = static_cast<EGLSurface>(window->surface(m_eglConfig, &err)); - if (eglSurface == EGL_NO_SURFACE) { - if (err == EGL_CONTEXT_LOST) { - m_eglContext = EGL_NO_CONTEXT; - qCDebug(lcQpaGl) << "Got EGL context lost in createWindowSurface() for context" << this; - } - return; - } - - bool ok = QWindowsEGLStaticContext::libEGL.eglSwapBuffers(m_eglDisplay, eglSurface); - if (!ok) { - err = QWindowsEGLStaticContext::libEGL.eglGetError(); - if (err == EGL_CONTEXT_LOST) { - m_eglContext = EGL_NO_CONTEXT; - qCDebug(lcQpaGl) << "Got EGL context lost in eglSwapBuffers()"; - } else { - qWarning("%s: Failed to swap buffers. eglError: %d, this: %p", __FUNCTION__, err, this); - } - } -} - -QFunctionPointer QWindowsEGLContext::getProcAddress(const char *procName) -{ - QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); - - QFunctionPointer procAddress = nullptr; - - // Special logic for ANGLE extensions for blitFramebuffer and - // renderbufferStorageMultisample. In version 2 contexts the extensions - // must be used instead of the suffixless, version 3.0 functions. - if (m_format.majorVersion() < 3) { - if (!strcmp(procName, "glBlitFramebuffer") || !strcmp(procName, "glRenderbufferStorageMultisample")) { - char extName[32 + 5 + 1]; - strcpy(extName, procName); - strcat(extName, "ANGLE"); - procAddress = reinterpret_cast<QFunctionPointer>(QWindowsEGLStaticContext::libEGL.eglGetProcAddress(extName)); - } - } - - if (!procAddress) - procAddress = reinterpret_cast<QFunctionPointer>(QWindowsEGLStaticContext::libEGL.eglGetProcAddress(procName)); - - // We support AllGLFunctionsQueryable, which means this function must be able to - // return a function pointer for standard GLES2 functions too. These are not - // guaranteed to be queryable via eglGetProcAddress(). - if (!procAddress) { -#if defined(QT_STATIC) && !defined(QT_OPENGL_DYNAMIC) - static struct StdFunc { - const char *name; - void *func; - } standardFuncs[] = { - { "glBindTexture", (void *) ::glBindTexture }, - { "glBlendFunc", (void *) ::glBlendFunc }, - { "glClear", (void *) ::glClear }, - { "glClearColor", (void *) ::glClearColor }, - { "glClearStencil", (void *) ::glClearStencil }, - { "glColorMask", (void *) ::glColorMask }, - { "glCopyTexImage2D", (void *) ::glCopyTexImage2D }, - { "glCopyTexSubImage2D", (void *) ::glCopyTexSubImage2D }, - { "glCullFace", (void *) ::glCullFace }, - { "glDeleteTextures", (void *) ::glDeleteTextures }, - { "glDepthFunc", (void *) ::glDepthFunc }, - { "glDepthMask", (void *) ::glDepthMask }, - { "glDisable", (void *) ::glDisable }, - { "glDrawArrays", (void *) ::glDrawArrays }, - { "glDrawElements", (void *) ::glDrawElements }, - { "glEnable", (void *) ::glEnable }, - { "glFinish", (void *) ::glFinish }, - { "glFlush", (void *) ::glFlush }, - { "glFrontFace", (void *) ::glFrontFace }, - { "glGenTextures", (void *) ::glGenTextures }, - { "glGetBooleanv", (void *) ::glGetBooleanv }, - { "glGetError", (void *) ::glGetError }, - { "glGetFloatv", (void *) ::glGetFloatv }, - { "glGetIntegerv", (void *) ::glGetIntegerv }, - { "glGetString", (void *) ::glGetString }, - { "glGetTexParameterfv", (void *) ::glGetTexParameterfv }, - { "glGetTexParameteriv", (void *) ::glGetTexParameteriv }, - { "glHint", (void *) ::glHint }, - { "glIsEnabled", (void *) ::glIsEnabled }, - { "glIsTexture", (void *) ::glIsTexture }, - { "glLineWidth", (void *) ::glLineWidth }, - { "glPixelStorei", (void *) ::glPixelStorei }, - { "glPolygonOffset", (void *) ::glPolygonOffset }, - { "glReadPixels", (void *) ::glReadPixels }, - { "glScissor", (void *) ::glScissor }, - { "glStencilFunc", (void *) ::glStencilFunc }, - { "glStencilMask", (void *) ::glStencilMask }, - { "glStencilOp", (void *) ::glStencilOp }, - { "glTexImage2D", (void *) ::glTexImage2D }, - { "glTexParameterf", (void *) ::glTexParameterf }, - { "glTexParameterfv", (void *) ::glTexParameterfv }, - { "glTexParameteri", (void *) ::glTexParameteri }, - { "glTexParameteriv", (void *) ::glTexParameteriv }, - { "glTexSubImage2D", (void *) ::glTexSubImage2D }, - { "glViewport", (void *) ::glViewport }, - - { "glActiveTexture", (void *) ::glActiveTexture }, - { "glAttachShader", (void *) ::glAttachShader }, - { "glBindAttribLocation", (void *) ::glBindAttribLocation }, - { "glBindBuffer", (void *) ::glBindBuffer }, - { "glBindFramebuffer", (void *) ::glBindFramebuffer }, - { "glBindRenderbuffer", (void *) ::glBindRenderbuffer }, - { "glBlendColor", (void *) ::glBlendColor }, - { "glBlendEquation", (void *) ::glBlendEquation }, - { "glBlendEquationSeparate", (void *) ::glBlendEquationSeparate }, - { "glBlendFuncSeparate", (void *) ::glBlendFuncSeparate }, - { "glBufferData", (void *) ::glBufferData }, - { "glBufferSubData", (void *) ::glBufferSubData }, - { "glCheckFramebufferStatus", (void *) ::glCheckFramebufferStatus }, - { "glCompileShader", (void *) ::glCompileShader }, - { "glCompressedTexImage2D", (void *) ::glCompressedTexImage2D }, - { "glCompressedTexSubImage2D", (void *) ::glCompressedTexSubImage2D }, - { "glCreateProgram", (void *) ::glCreateProgram }, - { "glCreateShader", (void *) ::glCreateShader }, - { "glDeleteBuffers", (void *) ::glDeleteBuffers }, - { "glDeleteFramebuffers", (void *) ::glDeleteFramebuffers }, - { "glDeleteProgram", (void *) ::glDeleteProgram }, - { "glDeleteRenderbuffers", (void *) ::glDeleteRenderbuffers }, - { "glDeleteShader", (void *) ::glDeleteShader }, - { "glDetachShader", (void *) ::glDetachShader }, - { "glDisableVertexAttribArray", (void *) ::glDisableVertexAttribArray }, - { "glEnableVertexAttribArray", (void *) ::glEnableVertexAttribArray }, - { "glFramebufferRenderbuffer", (void *) ::glFramebufferRenderbuffer }, - { "glFramebufferTexture2D", (void *) ::glFramebufferTexture2D }, - { "glGenBuffers", (void *) ::glGenBuffers }, - { "glGenerateMipmap", (void *) ::glGenerateMipmap }, - { "glGenFramebuffers", (void *) ::glGenFramebuffers }, - { "glGenRenderbuffers", (void *) ::glGenRenderbuffers }, - { "glGetActiveAttrib", (void *) ::glGetActiveAttrib }, - { "glGetActiveUniform", (void *) ::glGetActiveUniform }, - { "glGetAttachedShaders", (void *) ::glGetAttachedShaders }, - { "glGetAttribLocation", (void *) ::glGetAttribLocation }, - { "glGetBufferParameteriv", (void *) ::glGetBufferParameteriv }, - { "glGetFramebufferAttachmentParameteriv", (void *) ::glGetFramebufferAttachmentParameteriv }, - { "glGetProgramiv", (void *) ::glGetProgramiv }, - { "glGetProgramInfoLog", (void *) ::glGetProgramInfoLog }, - { "glGetRenderbufferParameteriv", (void *) ::glGetRenderbufferParameteriv }, - { "glGetShaderiv", (void *) ::glGetShaderiv }, - { "glGetShaderInfoLog", (void *) ::glGetShaderInfoLog }, - { "glGetShaderPrecisionFormat", (void *) ::glGetShaderPrecisionFormat }, - { "glGetShaderSource", (void *) ::glGetShaderSource }, - { "glGetUniformfv", (void *) ::glGetUniformfv }, - { "glGetUniformiv", (void *) ::glGetUniformiv }, - { "glGetUniformLocation", (void *) ::glGetUniformLocation }, - { "glGetVertexAttribfv", (void *) ::glGetVertexAttribfv }, - { "glGetVertexAttribiv", (void *) ::glGetVertexAttribiv }, - { "glGetVertexAttribPointerv", (void *) ::glGetVertexAttribPointerv }, - { "glIsBuffer", (void *) ::glIsBuffer }, - { "glIsFramebuffer", (void *) ::glIsFramebuffer }, - { "glIsProgram", (void *) ::glIsProgram }, - { "glIsRenderbuffer", (void *) ::glIsRenderbuffer }, - { "glIsShader", (void *) ::glIsShader }, - { "glLinkProgram", (void *) ::glLinkProgram }, - { "glReleaseShaderCompiler", (void *) ::glReleaseShaderCompiler }, - { "glRenderbufferStorage", (void *) ::glRenderbufferStorage }, - { "glSampleCoverage", (void *) ::glSampleCoverage }, - { "glShaderBinary", (void *) ::glShaderBinary }, - { "glShaderSource", (void *) ::glShaderSource }, - { "glStencilFuncSeparate", (void *) ::glStencilFuncSeparate }, - { "glStencilMaskSeparate", (void *) ::glStencilMaskSeparate }, - { "glStencilOpSeparate", (void *) ::glStencilOpSeparate }, - { "glUniform1f", (void *) ::glUniform1f }, - { "glUniform1fv", (void *) ::glUniform1fv }, - { "glUniform1i", (void *) ::glUniform1i }, - { "glUniform1iv", (void *) ::glUniform1iv }, - { "glUniform2f", (void *) ::glUniform2f }, - { "glUniform2fv", (void *) ::glUniform2fv }, - { "glUniform2i", (void *) ::glUniform2i }, - { "glUniform2iv", (void *) ::glUniform2iv }, - { "glUniform3f", (void *) ::glUniform3f }, - { "glUniform3fv", (void *) ::glUniform3fv }, - { "glUniform3i", (void *) ::glUniform3i }, - { "glUniform3iv", (void *) ::glUniform3iv }, - { "glUniform4f", (void *) ::glUniform4f }, - { "glUniform4fv", (void *) ::glUniform4fv }, - { "glUniform4i", (void *) ::glUniform4i }, - { "glUniform4iv", (void *) ::glUniform4iv }, - { "glUniformMatrix2fv", (void *) ::glUniformMatrix2fv }, - { "glUniformMatrix3fv", (void *) ::glUniformMatrix3fv }, - { "glUniformMatrix4fv", (void *) ::glUniformMatrix4fv }, - { "glUseProgram", (void *) ::glUseProgram }, - { "glValidateProgram", (void *) ::glValidateProgram }, - { "glVertexAttrib1f", (void *) ::glVertexAttrib1f }, - { "glVertexAttrib1fv", (void *) ::glVertexAttrib1fv }, - { "glVertexAttrib2f", (void *) ::glVertexAttrib2f }, - { "glVertexAttrib2fv", (void *) ::glVertexAttrib2fv }, - { "glVertexAttrib3f", (void *) ::glVertexAttrib3f }, - { "glVertexAttrib3fv", (void *) ::glVertexAttrib3fv }, - { "glVertexAttrib4f", (void *) ::glVertexAttrib4f }, - { "glVertexAttrib4fv", (void *) ::glVertexAttrib4fv }, - { "glVertexAttribPointer", (void *) ::glVertexAttribPointer }, - - { "glClearDepthf", (void *) ::glClearDepthf }, - { "glDepthRangef", (void *) ::glDepthRangef } - }; - for (size_t i = 0; i < sizeof(standardFuncs) / sizeof(StdFunc); ++i) - if (!qstrcmp(procName, standardFuncs[i].name)) - return reinterpret_cast<QFunctionPointer>(standardFuncs[i].func); -#else - procAddress = reinterpret_cast<QFunctionPointer>(QWindowsEGLStaticContext::libGLESv2.resolve(procName)); -#endif -} - - if (QWindowsContext::verbose > 1) - qCDebug(lcQpaGl) << __FUNCTION__ << procName << QWindowsEGLStaticContext::libEGL.eglGetCurrentContext() << "returns" << procAddress; - - return procAddress; -} - -static QVector<EGLint> createConfigAttributesFromFormat(const QSurfaceFormat &format) -{ - int redSize = format.redBufferSize(); - int greenSize = format.greenBufferSize(); - int blueSize = format.blueBufferSize(); - int alphaSize = format.alphaBufferSize(); - int depthSize = format.depthBufferSize(); - int stencilSize = format.stencilBufferSize(); - int sampleCount = format.samples(); - - QVector<EGLint> configAttributes; - configAttributes.reserve(16); - - configAttributes.append(EGL_RED_SIZE); - configAttributes.append(redSize > 0 ? redSize : 0); - - configAttributes.append(EGL_GREEN_SIZE); - configAttributes.append(greenSize > 0 ? greenSize : 0); - - configAttributes.append(EGL_BLUE_SIZE); - configAttributes.append(blueSize > 0 ? blueSize : 0); - - configAttributes.append(EGL_ALPHA_SIZE); - configAttributes.append(alphaSize > 0 ? alphaSize : 0); - - configAttributes.append(EGL_DEPTH_SIZE); - configAttributes.append(depthSize > 0 ? depthSize : 0); - - configAttributes.append(EGL_STENCIL_SIZE); - configAttributes.append(stencilSize > 0 ? stencilSize : 0); - - configAttributes.append(EGL_SAMPLES); - configAttributes.append(sampleCount > 0 ? sampleCount : 0); - - configAttributes.append(EGL_SAMPLE_BUFFERS); - configAttributes.append(sampleCount > 0); - - return configAttributes; -} - -static bool reduceConfigAttributes(QVector<EGLint> *configAttributes) -{ - int i = -1; - - i = configAttributes->indexOf(EGL_SWAP_BEHAVIOR); - if (i >= 0) { - configAttributes->remove(i,2); - } - - i = configAttributes->indexOf(EGL_BUFFER_SIZE); - if (i >= 0) { - if (configAttributes->at(i+1) == 16) { - configAttributes->remove(i,2); - return true; - } - } - - i = configAttributes->indexOf(EGL_SAMPLES); - if (i >= 0) { - EGLint value = configAttributes->value(i+1, 0); - if (value > 1) - configAttributes->replace(i+1, qMin(EGLint(16), value / 2)); - else - configAttributes->remove(i, 2); - return true; - } - - i = configAttributes->indexOf(EGL_SAMPLE_BUFFERS); - if (i >= 0) { - configAttributes->remove(i,2); - return true; - } - - i = configAttributes->indexOf(EGL_ALPHA_SIZE); - if (i >= 0) { - configAttributes->remove(i,2); -#if defined(EGL_BIND_TO_TEXTURE_RGBA) && defined(EGL_BIND_TO_TEXTURE_RGB) - i = configAttributes->indexOf(EGL_BIND_TO_TEXTURE_RGBA); - if (i >= 0) { - configAttributes->replace(i,EGL_BIND_TO_TEXTURE_RGB); - configAttributes->replace(i+1,true); - - } -#endif - return true; - } - - i = configAttributes->indexOf(EGL_STENCIL_SIZE); - if (i >= 0) { - if (configAttributes->at(i + 1) > 1) - configAttributes->replace(i + 1, 1); - else - configAttributes->remove(i, 2); - return true; - } - - i = configAttributes->indexOf(EGL_DEPTH_SIZE); - if (i >= 0) { - if (configAttributes->at(i + 1) > 1) - configAttributes->replace(i + 1, 1); - else - configAttributes->remove(i, 2); - return true; - } -#ifdef EGL_BIND_TO_TEXTURE_RGB - i = configAttributes->indexOf(EGL_BIND_TO_TEXTURE_RGB); - if (i >= 0) { - configAttributes->remove(i,2); - return true; - } -#endif - - return false; -} - -EGLConfig QWindowsEGLContext::chooseConfig(const QSurfaceFormat &format) -{ - QVector<EGLint> configureAttributes = createConfigAttributesFromFormat(format); - configureAttributes.append(EGL_SURFACE_TYPE); - configureAttributes.append(EGL_WINDOW_BIT); - configureAttributes.append(EGL_RENDERABLE_TYPE); - configureAttributes.append(EGL_OPENGL_ES2_BIT); - configureAttributes.append(EGL_NONE); - - EGLDisplay display = m_staticContext->display(); - EGLConfig cfg = nullptr; - do { - // Get the number of matching configurations for this set of properties. - EGLint matching = 0; - if (!QWindowsEGLStaticContext::libEGL.eglChooseConfig(display, configureAttributes.constData(), nullptr, 0, &matching) || !matching) - continue; - - // Fetch all of the matching configurations and find the - // first that matches the pixel format we wanted. - int i = configureAttributes.indexOf(EGL_RED_SIZE); - int confAttrRed = configureAttributes.at(i+1); - i = configureAttributes.indexOf(EGL_GREEN_SIZE); - int confAttrGreen = configureAttributes.at(i+1); - i = configureAttributes.indexOf(EGL_BLUE_SIZE); - int confAttrBlue = configureAttributes.at(i+1); - i = configureAttributes.indexOf(EGL_ALPHA_SIZE); - int confAttrAlpha = i == -1 ? 0 : configureAttributes.at(i+1); - - QVector<EGLConfig> configs(matching); - QWindowsEGLStaticContext::libEGL.eglChooseConfig(display, configureAttributes.constData(), configs.data(), configs.size(), &matching); - if (!cfg && matching > 0) - cfg = configs.constFirst(); - - EGLint red = 0; - EGLint green = 0; - EGLint blue = 0; - EGLint alpha = 0; - for (const EGLConfig &config : configs) { - if (confAttrRed) - QWindowsEGLStaticContext::libEGL.eglGetConfigAttrib(display, config, EGL_RED_SIZE, &red); - if (confAttrGreen) - QWindowsEGLStaticContext::libEGL.eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &green); - if (confAttrBlue) - QWindowsEGLStaticContext::libEGL.eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blue); - if (confAttrAlpha) - QWindowsEGLStaticContext::libEGL.eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alpha); - - if (red == confAttrRed && green == confAttrGreen - && blue == confAttrBlue && alpha == confAttrAlpha) - return config; - } - } while (reduceConfigAttributes(&configureAttributes)); - - if (!cfg) - qWarning("Cannot find EGLConfig, returning null config"); - - return cfg; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h deleted file mode 100644 index d96e266159..0000000000 --- a/src/plugins/platforms/windows/qwindowseglcontext.h +++ /dev/null @@ -1,176 +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 QWINDOWSEGLCONTEXT_H -#define QWINDOWSEGLCONTEXT_H - -#include "qwindowsopenglcontext.h" -#include "qwindowsopengltester.h" -#include <EGL/egl.h> - -QT_BEGIN_NAMESPACE - -struct QWindowsLibEGL -{ - bool init(); - - EGLint (EGLAPIENTRY * eglGetError)(void); - EGLDisplay (EGLAPIENTRY * eglGetDisplay)(EGLNativeDisplayType display_id); - EGLBoolean (EGLAPIENTRY * eglInitialize)(EGLDisplay dpy, EGLint *major, EGLint *minor); - EGLBoolean (EGLAPIENTRY * eglTerminate)(EGLDisplay dpy); - EGLBoolean (EGLAPIENTRY * eglChooseConfig)(EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config); - EGLBoolean (EGLAPIENTRY * eglGetConfigAttrib)(EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value); - EGLSurface (EGLAPIENTRY * eglCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, - EGLNativeWindowType win, - const EGLint *attrib_list); - EGLSurface (EGLAPIENTRY * eglCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list); - EGLBoolean (EGLAPIENTRY * eglDestroySurface)(EGLDisplay dpy, EGLSurface surface); - EGLBoolean (EGLAPIENTRY * eglBindAPI)(EGLenum api); - EGLBoolean (EGLAPIENTRY * eglSwapInterval)(EGLDisplay dpy, EGLint interval); - EGLContext (EGLAPIENTRY * eglCreateContext)(EGLDisplay dpy, EGLConfig config, - EGLContext share_context, - const EGLint *attrib_list); - EGLBoolean (EGLAPIENTRY * eglDestroyContext)(EGLDisplay dpy, EGLContext ctx); - EGLBoolean (EGLAPIENTRY * eglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx); - EGLContext (EGLAPIENTRY * eglGetCurrentContext)(void); - EGLSurface (EGLAPIENTRY * eglGetCurrentSurface)(EGLint readdraw); - EGLDisplay (EGLAPIENTRY * eglGetCurrentDisplay)(void); - EGLBoolean (EGLAPIENTRY * eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface); - QFunctionPointer (EGLAPIENTRY *eglGetProcAddress)(const char *procname); - - EGLDisplay (EGLAPIENTRY * eglGetPlatformDisplayEXT)(EGLenum platform, void *native_display, const EGLint *attrib_list); - -private: -#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) - void *resolve(const char *name); - HMODULE m_lib; -#endif -}; - -struct QWindowsLibGLESv2 -{ - bool init(); - -#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) - void *moduleHandle() const { return m_lib; } -#else - void *moduleHandle() const { return nullptr; } -#endif - - const GLubyte * (APIENTRY * glGetString)(GLenum name); - -#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC) - void *resolve(const char *name); -private: - HMODULE m_lib; -#endif -}; - -class QWindowsEGLStaticContext : public QWindowsStaticOpenGLContext -{ - Q_DISABLE_COPY_MOVE(QWindowsEGLStaticContext) - -public: - static QWindowsEGLStaticContext *create(QWindowsOpenGLTester::Renderers preferredType); - ~QWindowsEGLStaticContext() override; - - EGLDisplay display() const { return m_display; } - - QWindowsOpenGLContext *createContext(QOpenGLContext *context) override; - void *moduleHandle() const override { return libGLESv2.moduleHandle(); } - QOpenGLContext::OpenGLModuleType moduleType() const override { return QOpenGLContext::LibGLES; } - - void *createWindowSurface(void *nativeWindow, void *nativeConfig, int *err) override; - void destroyWindowSurface(void *nativeSurface) override; - - QSurfaceFormat formatFromConfig(EGLDisplay display, EGLConfig config, const QSurfaceFormat &referenceFormat); - - static QWindowsLibEGL libEGL; - static QWindowsLibGLESv2 libGLESv2; - -private: - explicit QWindowsEGLStaticContext(EGLDisplay display); - static bool initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, - EGLDisplay *display, EGLint *major, EGLint *minor); - - const EGLDisplay m_display; -}; - -class QWindowsEGLContext : public QWindowsOpenGLContext -{ -public: - QWindowsEGLContext(QWindowsEGLStaticContext *staticContext, - const QSurfaceFormat &format, - QPlatformOpenGLContext *share); - ~QWindowsEGLContext() override; - - bool makeCurrent(QPlatformSurface *surface) override; - void doneCurrent() override; - void swapBuffers(QPlatformSurface *surface) override; - QFunctionPointer getProcAddress(const char *procName) override; - - QSurfaceFormat format() const override { return m_format; } - bool isSharing() const override { return m_shareContext != EGL_NO_CONTEXT; } - bool isValid() const override { return m_eglContext != EGL_NO_CONTEXT; } - - void *nativeContext() const override { return m_eglContext; } - void *nativeDisplay() const override { return m_eglDisplay; } - void *nativeConfig() const override { return m_eglConfig; } - -private: - EGLConfig chooseConfig(const QSurfaceFormat &format); - - QWindowsEGLStaticContext *m_staticContext; - EGLContext m_eglContext; - EGLContext m_shareContext; - EGLDisplay m_eglDisplay; - EGLConfig m_eglConfig; - QSurfaceFormat m_format; - EGLenum m_api = EGL_OPENGL_ES_API; - int m_swapInterval = -1; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSEGLCONTEXT_H diff --git a/src/plugins/platforms/windows/qwindowsgdiintegration.cpp b/src/plugins/platforms/windows/qwindowsgdiintegration.cpp index c88f669eb5..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" 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 d9521e7e08..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" @@ -44,10 +8,10 @@ #include <QtCore/qdebug.h> #include <QtCore/qsysinfo.h> +#include <QtGui/qcolorspace.h> #include <QtGui/qguiapplication.h> #include <qpa/qplatformnativeinterface.h> -#include <QtPlatformHeaders/qwglnativecontext.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")); @@ -225,6 +195,11 @@ QWindowsOpenGLContext *QOpenGLStaticContext::createContext(QOpenGLContext *conte return new QWindowsGLContext(this, context); } +QWindowsOpenGLContext *QOpenGLStaticContext::createContext(HGLRC context, HWND window) +{ + return new QWindowsGLContext(this, context, window); +} + template <class MaskType, class FlagType> inline bool testFlag(MaskType mask, FlagType flag) { return (mask & MaskType(flag)) != 0; @@ -569,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; @@ -584,10 +562,10 @@ static int choosePixelFormat(HDC hdc, iAttributes[i++] = FALSE; } // must be the last - bool srgbRequested = format.colorSpace() == QSurfaceFormat::sRGBColorSpace; - int srgbValuePosition = 0; + bool srgbRequested = format.colorSpace() == QColorSpace::SRgb; + int srgbCapableKeyPosition = 0; if (srgbRequested) { - srgbValuePosition = i; + srgbCapableKeyPosition = i; iAttributes[i++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT; iAttributes[i++] = TRUE; } @@ -601,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) { @@ -610,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; } @@ -706,10 +682,10 @@ static QSurfaceFormat if (hasSampleBuffers) { result.setSamples(iValues[13]); if (hasSrgbSupport && iValues[14]) - result.setColorSpace(QSurfaceFormat::sRGBColorSpace); + result.setColorSpace(QColorSpace::SRgb); } else { if (hasSrgbSupport && iValues[12]) - result.setColorSpace(QSurfaceFormat::sRGBColorSpace); + result.setColorSpace(QColorSpace::SRgb); } if (additionalIn) { if (iValues[7]) @@ -802,7 +778,7 @@ static HGLRC createContext(const QOpenGLStaticContext &staticContext, static inline HWND createDummyGLWindow() { return QWindowsContext::instance()-> - createDummyWindow(QStringLiteral("QtOpenGLDummyWindow"), + createDummyWindow(QStringLiteral("OpenGLDummyWindow"), L"OpenGLDummyWindow", nullptr, WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); } @@ -858,7 +834,6 @@ static inline QOpenGLContextData createDummyWindowOpenGLContextData() context and to apply to a QSurfaceFormat. \internal - \ingroup qt-lighthouse-win */ QWindowsOpenGLContextFormat QWindowsOpenGLContextFormat::current() @@ -913,7 +888,6 @@ void QWindowsOpenGLContextFormat::apply(QSurfaceFormat *format) const is a current GL context. \internal - \ingroup qt-lighthouse-win */ class QOpenGLTemporaryContext @@ -946,7 +920,6 @@ QOpenGLTemporaryContext::~QOpenGLTemporaryContext() /*! \class QWindowsOpenGLAdditionalFormat \brief Additional format information that is not in QSurfaceFormat - \ingroup qt-lighthouse-win */ /*! @@ -964,7 +937,6 @@ QOpenGLTemporaryContext::~QOpenGLTemporaryContext() \sa QWindowsGLContext \internal - \ingroup qt-lighthouse-win */ #define SAMPLE_BUFFER_EXTENSION "GL_ARB_multisample" @@ -1051,66 +1023,15 @@ QOpenGLStaticContext *QOpenGLStaticContext::create(bool softwareRendering) QOpenGLContextData and are released in doneCurrent(). \internal - \ingroup qt-lighthouse-win */ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, QOpenGLContext *context) : - m_staticContext(staticContext), - m_context(context), - m_renderingContext(nullptr), - m_pixelFormat(0), - m_extensionsUsed(false), - m_swapInterval(-1), - m_ownsContext(true), - m_getGraphicsResetStatus(nullptr), - m_lost(false) + m_staticContext(staticContext) { if (!m_staticContext) // Something went very wrong. Stop here, isValid() will return false. return; - QVariant nativeHandle = context->nativeHandle(); - if (!nativeHandle.isNull()) { - // Adopt and existing context. - if (!nativeHandle.canConvert<QWGLNativeContext>()) { - qWarning("QWindowsGLContext: Requires a QWGLNativeContext"); - return; - } - auto handle = nativeHandle.value<QWGLNativeContext>(); - HGLRC wglcontext = handle.context(); - HWND wnd = handle.window(); - if (!wglcontext || !wnd) { - qWarning("QWindowsGLContext: No context and window given"); - return; - } - - HDC dc = GetDC(wnd); - // A window with an associated pixel format is mandatory. - // When no SetPixelFormat() call has been made, the following will fail. - m_pixelFormat = GetPixelFormat(dc); - bool ok = m_pixelFormat != 0; - if (!ok) - qWarning("QWindowsGLContext: Failed to get pixel format"); - ok = DescribePixelFormat(dc, m_pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &m_obtainedPixelFormatDescriptor); - if (!ok) { - qWarning("QWindowsGLContext: Failed to describe pixel format"); - } else { - QWindowsOpenGLAdditionalFormat obtainedAdditional; - m_obtainedFormat = GDI::qSurfaceFormatFromPixelFormat(m_obtainedPixelFormatDescriptor, &obtainedAdditional); - m_renderingContext = wglcontext; - ok = updateObtainedParams(dc); - } - - ReleaseDC(wnd, dc); - - if (ok) - m_ownsContext = false; - else - m_renderingContext = nullptr; - - return; - } - QSurfaceFormat format = context->format(); if (format.renderableType() == QSurfaceFormat::DefaultRenderableType) format.setRenderableType(QSurfaceFormat::OpenGL); @@ -1212,11 +1133,6 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, } while (false); - // Make the HGLRC retrievable via QOpenGLContext::nativeHandle(). - // Do not provide the window since it is the dummy one and it is about to disappear. - if (m_renderingContext) - context->setNativeHandle(QVariant::fromValue<QWGLNativeContext>(QWGLNativeContext(m_renderingContext, nullptr))); - if (hdc) ReleaseDC(dummyWindow, hdc); if (dummyWindow) @@ -1230,6 +1146,37 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, << "\n HGLRC=" << m_renderingContext; } +QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext, HGLRC wglcontext, HWND wnd) + : m_staticContext(staticContext) +{ + if (!m_staticContext) // Something went very wrong. Stop here, isValid() will return false. + return; + + HDC dc = GetDC(wnd); + // A window with an associated pixel format is mandatory. + // When no SetPixelFormat() call has been made, the following will fail. + m_pixelFormat = GetPixelFormat(dc); + bool ok = m_pixelFormat != 0; + if (!ok) + qWarning("QWindowsGLContext: Failed to get pixel format"); + ok = DescribePixelFormat(dc, m_pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &m_obtainedPixelFormatDescriptor); + if (!ok) { + qWarning("QWindowsGLContext: Failed to describe pixel format"); + } else { + QWindowsOpenGLAdditionalFormat obtainedAdditional; + m_obtainedFormat = GDI::qSurfaceFormatFromPixelFormat(m_obtainedPixelFormatDescriptor, &obtainedAdditional); + m_renderingContext = wglcontext; + ok = updateObtainedParams(dc); + } + + ReleaseDC(wnd, dc); + + if (ok) + m_ownsContext = false; + else + m_renderingContext = nullptr; +} + QWindowsGLContext::~QWindowsGLContext() { if (m_renderingContext && m_ownsContext) @@ -1315,17 +1262,19 @@ 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; } - return QOpenGLStaticContext::opengl32.wglMakeCurrent(contextData->hdc, contextData->renderingContext); + const bool success = QOpenGLStaticContext::opengl32.wglMakeCurrent(contextData->hdc, contextData->renderingContext); + if (!success) + qErrnoWarning("%s: wglMakeCurrent() failed for existing context data", __FUNCTION__); + return success; } // Create a new entry. const QOpenGLContextData newContext(m_renderingContext, hwnd, GetDC(hwnd)); @@ -1353,6 +1302,8 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface) qCDebug(lcQpaGl) << "makeCurrent(): context loss detected" << this; // Drop the surface. Will recreate on the next makeCurrent. window->invalidateSurface(); + } else { + qErrnoWarning("%s: wglMakeCurrent() failed", __FUNCTION__); } } diff --git a/src/plugins/platforms/windows/qwindowsglcontext.h b/src/plugins/platforms/windows/qwindowsglcontext.h index 8794368fe4..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 @@ -174,6 +138,7 @@ public: static QByteArray getGlString(unsigned int which); QWindowsOpenGLContext *createContext(QOpenGLContext *context) override; + QWindowsOpenGLContext *createContext(HGLRC context, HWND window) override; void *moduleHandle() const override { return opengl32.moduleHandle(); } QOpenGLContext::OpenGLModuleType moduleType() const override { return QOpenGLContext::LibGL; } @@ -199,12 +164,14 @@ public: static QWindowsOpengl32DLL opengl32; }; -class QWindowsGLContext : public QWindowsOpenGLContext +class QWindowsGLContext : public QWindowsOpenGLContext, public QNativeInterface::QWGLContext { public: explicit QWindowsGLContext(QOpenGLStaticContext *staticContext, QOpenGLContext *context); + explicit QWindowsGLContext(QOpenGLStaticContext *staticContext, HGLRC context, HWND window); + ~QWindowsGLContext() override; - bool isSharing() const override { return m_context->shareHandle(); } + bool isSharing() const override { return context()->shareHandle(); } bool isValid() const override { return m_renderingContext && !m_lost; } QSurfaceFormat format() const override { return m_obtainedFormat; } @@ -219,7 +186,7 @@ public: HGLRC renderingContext() const { return m_renderingContext; } - void *nativeContext() const override { return m_renderingContext; } + HGLRC nativeContext() const override { return m_renderingContext; } private: typedef GLenum (APIENTRY *GlGetGraphicsResetStatusArbType)(); @@ -227,18 +194,17 @@ private: inline void releaseDCs(); bool updateObtainedParams(HDC hdc, int *obtainedSwapInterval = nullptr); - QOpenGLStaticContext *m_staticContext; - QOpenGLContext *m_context; + QOpenGLStaticContext *m_staticContext = nullptr; QSurfaceFormat m_obtainedFormat; - HGLRC m_renderingContext; + HGLRC m_renderingContext = nullptr; std::vector<QOpenGLContextData> m_windowContexts; PIXELFORMATDESCRIPTOR m_obtainedPixelFormatDescriptor; - int m_pixelFormat; - bool m_extensionsUsed; - int m_swapInterval; - bool m_ownsContext; - GlGetGraphicsResetStatusArbType m_getGraphicsResetStatus; - bool m_lost; + int m_pixelFormat = 0; + bool m_extensionsUsed = false; + int m_swapInterval = -1; + bool m_ownsContext = true; + GlGetGraphicsResetStatusArbType m_getGraphicsResetStatus = nullptr; + bool m_lost = false; }; #endif QT_END_NAMESPACE 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 19d632dc10..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> @@ -160,7 +123,6 @@ Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id); // from qlocale_win.cpp needs to be checked (mouse grab might interfere with candidate window). \internal - \ingroup qt-lighthouse-win */ @@ -199,7 +161,6 @@ bool QWindowsInputContext::hasCapability(Capability capability) const void QWindowsInputContext::reset() { - QPlatformInputContext::reset(); if (!m_compositionContext.hwnd) return; qCDebug(lcQpaInputMethods) << __FUNCTION__; @@ -276,24 +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"); - if (imModuleEmpty - && 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 09117f663d..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" @@ -48,9 +12,14 @@ #include "qwindowsscreen.h" #include "qwindowstheme.h" #include "qwindowsservices.h" +#include <QtGui/private/qtgui-config_p.h> +#if QT_CONFIG(directwrite3) +#include <QtGui/private/qwindowsdirectwritefontdatabase_p.h> +#endif #ifndef QT_NO_FREETYPE -# include <QtFontDatabaseSupport/private/qwindowsfontdatabase_ft_p.h> +# include <QtGui/private/qwindowsfontdatabase_ft_p.h> #endif +#include <QtGui/private/qwindowsfontdatabase_p.h> #if QT_CONFIG(clipboard) # include "qwindowsclipboard.h" # if QT_CONFIG(draganddrop) @@ -68,28 +37,40 @@ #if QT_CONFIG(sessionmanager) # include "qwindowssessionmanager.h" #endif -#include <QtGui/qtouchdevice.h> +#include <QtGui/qpointingdevice.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qhighdpiscaling_p.h> #include <QtGui/qpa/qplatforminputcontextfactory_p.h> #include <QtGui/qpa/qplatformcursor.h> -#include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h> +#include <QtGui/private/qwindowsguieventdispatcher_p.h> #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_OPENGL_ES_2) || defined(QT_OPENGL_DYNAMIC) -# include "qwindowseglcontext.h" -#endif -#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) +#if !defined(QT_NO_OPENGL) # include "qwindowsglcontext.h" #endif #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); @@ -97,46 +78,16 @@ 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. - - \ingroup qt-lighthouse-win -*/ +using namespace Qt::StringLiterals; struct QWindowsIntegrationPrivate { Q_DISABLE_COPY_MOVE(QWindowsIntegrationPrivate) - explicit QWindowsIntegrationPrivate(const QStringList ¶mList); + explicit QWindowsIntegrationPrivate() = default; ~QWindowsIntegrationPrivate(); + void parseOptions(QWindowsIntegration *q, const QStringList ¶mList); + unsigned m_options = 0; QWindowsContext m_context; QPlatformFontDatabase *m_fontDatabase = nullptr; @@ -158,17 +109,17 @@ 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; - if (valueLength < 1 || !parameter.startsWith(option) || parameter.at(option.size()) != QLatin1Char('=')) + if (valueLength < 1 || !parameter.startsWith(option) || parameter.at(option.size()) != u'=') return false; bool ok; - const QStringRef valueRef = parameter.rightRef(valueLength); + 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" @@ -180,45 +131,60 @@ bool parseIntOption(const QString ¶meter,const QLatin1String &option, return true; } +using DarkModeHandlingFlag = QNativeInterface::Private::QWindowsApplication::DarkModeHandlingFlag; +using DarkModeHandling = QNativeInterface::Private::QWindowsApplication::DarkModeHandling; + 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(QLatin1String("fontengine="))) { - if (param.endsWith(QLatin1String("freetype"))) { + if (param.startsWith(u"fontengine=")) { + if (param.endsWith(u"gdi")) { + options |= QWindowsIntegration::FontDatabaseGDI; + } else if (param.endsWith(u"freetype")) { options |= QWindowsIntegration::FontDatabaseFreeType; - } else if (param.endsWith(QLatin1String("native"))) { + } else if (param.endsWith(u"native")) { options |= QWindowsIntegration::FontDatabaseNative; } - } else if (param.startsWith(QLatin1String("dialogs="))) { - if (param.endsWith(QLatin1String("xp"))) { + } else if (param.startsWith(u"dialogs=")) { + if (param.endsWith(u"xp")) { options |= QWindowsIntegration::XpNativeDialogs; - } else if (param.endsWith(QLatin1String("none"))) { + } else if (param.endsWith(u"none")) { options |= QWindowsIntegration::NoNativeDialogs; } - } else if (param == QLatin1String("altgr")) { + } else if (param == u"altgr") { options |= QWindowsIntegration::DetectAltGrModifier; - } else if (param == QLatin1String("gl=gdi")) { + } else if (param == u"gl=gdi") { options |= QWindowsIntegration::DisableArb; - } else if (param == QLatin1String("nodirectwrite")) { + } else if (param == u"nodirectwrite") { options |= QWindowsIntegration::DontUseDirectWriteFonts; - } else if (param == QLatin1String("nocolorfonts")) { + } else if (param == u"nocolorfonts") { options |= QWindowsIntegration::DontUseColorFonts; - } else if (param == QLatin1String("nomousefromtouch")) { + } 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::ProcessPerMonitorDpiAware, dpiAwareness)) { - } else if (param == QLatin1String("menus=native")) { + } 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 == QLatin1String("menus=none")) { + } else if (param == u"menus=none") { options |= QWindowsIntegration::NoNativeMenus; - } else if (param == QLatin1String("nowmpointer")) { + } else if (param == u"nowmpointer") { options |= QWindowsIntegration::DontUseWMPointer; - } else if (param == QLatin1String("reverse")) { + } 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); } else { qWarning() << "Unknown option" << param; } @@ -226,31 +192,33 @@ static inline unsigned parseOptions(const QStringList ¶mList, return options; } -QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(const QStringList ¶mList) +void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStringList ¶mList) { initOpenGlBlacklistResources(); static bool dpiAwarenessSet = false; + // Default to per-monitor-v2 awareness (if available) + QtWindows::DpiAwareness dpiAwareness = QtWindows::DpiAwareness::PerMonitorVersion2; + int tabletAbsoluteRange = -1; - // Default to per-monitor awareness to avoid being scaled when monitors with different DPI - // are connected to Windows 8.1 - QtWindows::ProcessDpiAwareness dpiAwareness = QtWindows::ProcessPerMonitorDpiAware; - m_options = parseOptions(paramList, &tabletAbsoluteRange, &dpiAwareness); + DarkModeHandling darkModeHandling = DarkModeHandlingFlag::DarkModeWindowFrames + | DarkModeHandlingFlag::DarkModeStyle; + m_options = ::parseOptions(paramList, &tabletAbsoluteRange, &dpiAwareness, &darkModeHandling); + q->setDarkModeHandling(darkModeHandling); QWindowsFontDatabase::setFontOptions(m_options); + if (tabletAbsoluteRange >= 0) + QWindowsContext::setTabletAbsoluteRange(tabletAbsoluteRange); - if (m_context.initPointer(m_options)) { + if (m_context.initPointer(m_options)) QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents); - } else { - m_context.initTablet(m_options); - if (tabletAbsoluteRange >= 0) - m_context.setTabletAbsoluteRange(tabletAbsoluteRange); - } + 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)) { m_context.setProcessDpiAwareness(dpiAwareness); - qCDebug(lcQpaWindows) - << __FUNCTION__ << "DpiAwareness=" << dpiAwareness + qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness(); } dpiAwarenessSet = true; @@ -270,13 +238,14 @@ QWindowsIntegrationPrivate::~QWindowsIntegrationPrivate() QWindowsIntegration *QWindowsIntegration::m_instance = nullptr; QWindowsIntegration::QWindowsIntegration(const QStringList ¶mList) : - d(new QWindowsIntegrationPrivate(paramList)) + d(new QWindowsIntegrationPrivate) { m_instance = this; + d->parseOptions(this, paramList); #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); } @@ -287,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 @@ -317,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); } @@ -327,23 +298,27 @@ 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; } QWindowsWindowData requested; requested.flags = window->flags(); - requested.geometry = QHighDpi::toNativePixels(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); + requested.geometry = window->isTopLevel() + ? QHighDpi::toNativePixels(window->geometry(), window) + : QHighDpi::toNativeLocalPosition(window->geometry(), window); + 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) @@ -380,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; } @@ -408,13 +383,6 @@ QWindowsStaticOpenGLContext *QWindowsStaticOpenGLContext::doCreate() } qCWarning(lcQpaGl, "System OpenGL failed. Falling back to Software OpenGL."); return QOpenGLStaticContext::create(true); - // If ANGLE is requested, use it, don't try anything else. - case QWindowsOpenGLTester::AngleRendererD3d9: - case QWindowsOpenGLTester::AngleRendererD3d11: - case QWindowsOpenGLTester::AngleRendererD3d11Warp: - return QWindowsEGLStaticContext::create(requestedRenderer); - case QWindowsOpenGLTester::Gles: - return QWindowsEGLStaticContext::create(requestedRenderer); case QWindowsOpenGLTester::SoftwareRasterizer: if (QWindowsStaticOpenGLContext *swCtx = QOpenGLStaticContext::create(true)) return swCtx; @@ -440,17 +408,8 @@ QWindowsStaticOpenGLContext *QWindowsStaticOpenGLContext::doCreate() return glCtx; } } - if (QWindowsOpenGLTester::Renderers glesRenderers = supportedRenderers & QWindowsOpenGLTester::GlesMask) { - if (QWindowsEGLStaticContext *eglCtx = QWindowsEGLStaticContext::create(glesRenderers)) - return eglCtx; - } return QOpenGLStaticContext::create(true); -#elif defined(QT_OPENGL_ES_2) - QWindowsOpenGLTester::Renderers glesRenderers = QWindowsOpenGLTester::requestedGlesRenderer(); - if (glesRenderers == QWindowsOpenGLTester::InvalidRenderer) - glesRenderers = QWindowsOpenGLTester::supportedRenderers(QWindowsOpenGLTester::AngleRendererD3d11); - return QWindowsEGLStaticContext::create(glesRenderers); -#elif !defined(QT_NO_OPENGL) +#else return QOpenGLStaticContext::create(); #endif } @@ -464,18 +423,16 @@ 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; } QOpenGLContext::OpenGLModuleType QWindowsIntegration::openGLModuleType() { -#if defined(QT_OPENGL_ES_2) - return QOpenGLContext::LibGLES; -#elif !defined(QT_OPENGL_DYNAMIC) +#if !defined(QT_OPENGL_DYNAMIC) return QOpenGLContext::LibGL; #else if (const QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) @@ -484,6 +441,33 @@ QOpenGLContext::OpenGLModuleType QWindowsIntegration::openGLModuleType() #endif } +HMODULE QWindowsIntegration::openGLModuleHandle() const +{ + if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) + return static_cast<HMODULE>(staticOpenGLContext->moduleHandle()); + + return nullptr; +} + +QOpenGLContext *QWindowsIntegration::createOpenGLContext(HGLRC ctx, HWND window, QOpenGLContext *shareContext) const +{ + if (!ctx || !window) + return nullptr; + + if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) { + 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.release()); + return context; + } + } + + return nullptr; +} + QWindowsStaticOpenGLContext *QWindowsIntegration::staticOpenGLContext() { QWindowsIntegration *integration = QWindowsIntegration::instance(); @@ -500,14 +484,17 @@ QWindowsStaticOpenGLContext *QWindowsIntegration::staticOpenGLContext() QPlatformFontDatabase *QWindowsIntegration::fontDatabase() const { if (!d->m_fontDatabase) { -#ifdef QT_NO_FREETYPE - d->m_fontDatabase = new QWindowsFontDatabase(); -#else // QT_NO_FREETYPE +#ifndef QT_NO_FREETYPE if (d->m_options & QWindowsIntegration::FontDatabaseFreeType) d->m_fontDatabase = new QWindowsFontDatabaseFT; else - d->m_fontDatabase = new QWindowsFontDatabase; #endif // QT_NO_FREETYPE +#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; } @@ -555,14 +542,9 @@ QVariant QWindowsIntegration::styleHint(QPlatformIntegration::StyleHint hint) co return QPlatformIntegration::styleHint(hint); } -Qt::KeyboardModifiers QWindowsIntegration::queryKeyboardModifiers() const +QPlatformKeyMapper *QWindowsIntegration::keyMapper() const { - return QWindowsKeyMapper::queryKeyboardModifiers(); -} - -QList<int> QWindowsIntegration::possibleKeys(const QKeyEvent *e) const -{ - return d->m_context.possibleKeys(e); + return d->m_context.keyMapper(); } #if QT_CONFIG(clipboard) @@ -609,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); } @@ -629,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 b49d21022b..c271207741 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -1,49 +1,19 @@ -/**************************************************************************** -** -** 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 +#include "qwindowsapplication.h" + #include <qpa/qplatformintegration.h> #include <QtCore/qscopedpointer.h> -#include <QtFontDatabaseSupport/private/qwindowsfontdatabase_p.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 @@ -53,6 +23,10 @@ class QWindowsWindow; class QWindowsStaticOpenGLContext; class QWindowsIntegration : public QPlatformIntegration +#ifndef QT_NO_OPENGL + , public QNativeInterface::Private::QWindowsGLIntegration +#endif + , public QWindowsApplication { Q_DISABLE_COPY_MOVE(QWindowsIntegration) public: @@ -70,7 +44,8 @@ public: NoNativeMenus = 0x200, DontUseWMPointer = 0x400, DetectAltGrModifier = 0x800, - RtlEnabled = 0x1000 + RtlEnabled = 0x1000, + FontDatabaseGDI = 0x2000 }; explicit QWindowsIntegration(const QStringList ¶mList); @@ -84,6 +59,10 @@ public: QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override; QOpenGLContext::OpenGLModuleType openGLModuleType() override; static QWindowsStaticOpenGLContext *staticOpenGLContext(); + + HMODULE openGLModuleHandle() const override; + QOpenGLContext *createOpenGLContext(HGLRC context, HWND window, + QOpenGLContext *shareContext) const override; #endif QAbstractEventDispatcher *createEventDispatcher() const override; void initialize() override; @@ -103,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; } @@ -112,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 @@ -127,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 44b7523fa6..0542473a4b 100644 --- a/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp +++ b/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp @@ -1,46 +1,12 @@ -/**************************************************************************** -** -** 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> + /*! \class QWindowsInternalMimeDataBase \brief Base for implementations of QInternalMimeData using a IDataObject COM object. @@ -56,11 +22,10 @@ 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 - \ingroup qt-lighthouse-win */ bool QWindowsInternalMimeData::hasFormat_sys(const QString &mime) const @@ -69,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; @@ -82,29 +47,28 @@ 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; return fmts; } -QVariant QWindowsInternalMimeData::retrieveData_sys(const QString &mimeType, - QVariant::Type type) const +QVariant QWindowsInternalMimeData::retrieveData_sys(const QString &mimeType, QMetaType type) const { IDataObject *pDataObj = retrieveDataObject(); if (!pDataObj) return QVariant(); QVariant result; - const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); - if (const QWindowsMime *converter = mc.converterToMime(mimeType, pDataObj)) + const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); + if (auto converter = mc.converterToMime(mimeType, pDataObj)) result = converter->convertToMime(mimeType, pDataObj, type); releaseDataObject(pDataObj); if (QWindowsContext::verbose) { - qCDebug(lcQpaMime) <<__FUNCTION__ << ' ' << mimeType << ' ' << type - << " returns " << result.type() - << (result.type() != QVariant::ByteArray ? result.toString() : QStringLiteral("<data>")); + qCDebug(lcQpaMime) <<__FUNCTION__ << ' ' << mimeType << ' ' << type.name() + << " returns " << result.metaType().name() + << (result.metaType().id() != QMetaType::QByteArray ? result.toString() : QStringLiteral("<data>")); } return result; } diff --git a/src/plugins/platforms/windows/qwindowsinternalmimedata.h b/src/plugins/platforms/windows/qwindowsinternalmimedata.h index dbc1ea3922..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 @@ -54,7 +18,7 @@ class QWindowsInternalMimeData : public QInternalMimeData { public: bool hasFormat_sys(const QString &mimetype) const override; QStringList formats_sys() const override; - QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const override; + QVariant retrieveData_sys(const QString &mimetype, QMetaType preferredType) const override; protected: virtual IDataObject *retrieveDataObject() const = 0; diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index e3edf7e81e..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" @@ -49,8 +13,9 @@ #include <private/qguiapplication_p.h> #include <private/qhighdpiscaling_p.h> #include <QtGui/qevent.h> -#include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.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 @@ -88,7 +53,6 @@ QT_BEGIN_NAMESPACE \class QWindowsKeyMapper \brief Translates Windows keys to QWindowSystemInterface events. \internal - \ingroup qt-lighthouse-win In addition, handles some special keys to display system menus, etc. The code originates from \c qkeymapper_win.cpp. @@ -124,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 { @@ -390,7 +362,8 @@ static const uint KeyTbl[] = { // Keyboard mapping table Qt::Key_MediaNext, // 176 0xB0 VK_MEDIA_NEXT_TRACK | Next Track key Qt::Key_MediaPrevious, //177 0xB1 VK_MEDIA_PREV_TRACK | Previous Track key Qt::Key_MediaStop, // 178 0xB2 VK_MEDIA_STOP | Stop Media key - Qt::Key_MediaPlay, // 179 0xB3 VK_MEDIA_PLAY_PAUSE | Play/Pause Media key + Qt::Key_MediaTogglePlayPause, + // 179 0xB3 VK_MEDIA_PLAY_PAUSE | Play/Pause Media key Qt::Key_LaunchMail, // 180 0xB4 VK_LAUNCH_MAIL | Start Mail key Qt::Key_LaunchMedia,// 181 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key Qt::Key_Launch0, // 182 0xB6 VK_LAUNCH_APP1 | Start Application 1 key @@ -541,7 +514,7 @@ static const Qt::KeyboardModifiers ModsTbl[] = { Qt::NoModifier, // Fall-back to raw Key_* }; static const size_t NumMods = sizeof ModsTbl / sizeof *ModsTbl; -Q_STATIC_ASSERT((NumMods == KeyboardLayoutItem::NumQtKeys)); +static_assert((NumMods == KeyboardLayoutItem::NumQtKeys)); #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const KeyboardLayoutItem &k) @@ -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,11 +878,11 @@ 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) - const QKeySequence sequence(Qt::Modifier(state) + qtKey); + const QKeySequence sequence(Qt::Modifier(state) | Qt::Key(qtKey)); return QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(sequence); # else return false; @@ -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; } @@ -1256,7 +1229,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, #ifndef QT_NO_SHORTCUT // Is Qt interested in the context menu key? if (modifiers == Qt::SHIFT && code == Qt::Key_F10 - && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(QKeySequence(Qt::SHIFT + Qt::Key_F10))) { + && !QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(QKeySequence(Qt::SHIFT | Qt::Key_F10))) { return false; } #endif // !QT_NO_SHORTCUT @@ -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 << int(Qt::Key_Enter + keyMods); + result << (Qt::Key_Enter | keyMods); return result; } - result << int(baseKey + 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) + 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 d20edd685e..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> @@ -56,7 +21,7 @@ QT_BEGIN_NAMESPACE \brief Windows native menu bar \list - \li \l{https://msdn.microsoft.com/de-de/library/windows/desktop/ms647553(v=vs.85).aspx#_win32_Menu_Creation_Functions}, + \li \l{https://docs.microsoft.com/en-us/windows/win32/menurc/about-menus}, \e{About Menus} \endlist @@ -65,15 +30,14 @@ QT_BEGIN_NAMESPACE Qt Widgets, either the containers or the items might be deleted first. \internal - \ingroup qt-lighthouse-win */ static uint nextId = 1; -// Find a QPlatformMenu[Item]* in a vector of QWindowsMenu[Item], where -// QVector::indexOf() cannot be used since it wants a QWindowsMenu[Item]* +// Find a QPlatformMenu[Item]* in a list of QWindowsMenu[Item], where +// QList::indexOf() cannot be used since it wants a QWindowsMenu[Item]* template <class Derived, class Needle> -static int indexOf(const QVector<Derived *> &v, const Needle *needle) +static int indexOf(const QList<Derived *> &v, const Needle *needle) { for (int i = 0, size = v.size(); i < size; ++i) { if (v.at(i) == needle) @@ -82,9 +46,9 @@ static int indexOf(const QVector<Derived *> &v, const Needle *needle) return -1; } -// Helper for inserting a QPlatformMenu[Item]* into a vector of QWindowsMenu[Item]. +// Helper for inserting a QPlatformMenu[Item]* into a list of QWindowsMenu[Item]. template <class Derived, class Base> -static int insertBefore(QVector<Derived *> *v, Base *newItemIn, const Base *before = nullptr) +static int insertBefore(QList<Derived *> *v, Base *newItemIn, const Base *before = nullptr) { int index = before ? indexOf(*v, before) : -1; if (index != -1) { @@ -176,7 +140,7 @@ static QWindowsMenu *findMenuByHandle(const Menu *menu, HMENU hMenu) } template <class MenuType> -static int findNextVisibleEntry(const QVector<MenuType *> &entries, int pos) +static int findNextVisibleEntry(const QList<MenuType *> &entries, int pos) { for (int i = pos, size = entries.size(); i < size; ++i) { if (entries.at(i)->isVisible()) @@ -266,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(); @@ -445,7 +407,7 @@ QString QWindowsMenuItem::nativeText() const QString result = m_text; #if QT_CONFIG(shortcut) if (!m_shortcut.isEmpty()) { - result += QLatin1Char('\t'); + result += u'\t'; result += m_shortcut.toString(QKeySequence::NativeText); } #endif @@ -794,20 +756,13 @@ QWindowsMenuBar *QWindowsMenuBar::menuBarOf(const QWindow *notYetCreatedWindow) ? qobject_cast<QWindowsMenuBar *>(menuBarV.value<QObject *>()) : nullptr; } -static inline void forceNcCalcSize(HWND hwnd) -{ - // Force WM_NCCALCSIZE to adjust margin: Does not appear to work? - SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, - SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); -} - void QWindowsMenuBar::install(QWindowsWindow *window) { const HWND hwnd = window->handle(); const BOOL result = SetMenu(hwnd, m_hMenuBar); if (result) { window->setMenuBar(this); - forceNcCalcSize(hwnd); + QWindowsContext::forceNcCalcSize(hwnd); } } @@ -817,7 +772,7 @@ void QWindowsMenuBar::removeFromWindow() const HWND hwnd = window->handle(); SetMenu(hwnd, nullptr); window->setMenuBar(nullptr); - forceNcCalcSize(hwnd); + QWindowsContext::forceNcCalcSize(hwnd); } } @@ -870,7 +825,7 @@ void QWindowsMenuBar::redraw() const #ifndef QT_NO_DEBUG_STREAM template <class M> /* Menu[Item] */ -static void formatTextSequence(QDebug &d, const QVector<M *> &v) +static void formatTextSequence(QDebug &d, const QList<M *> &v) { if (const int size = v.size()) { d << '[' << size << "]("; diff --git a/src/plugins/platforms/windows/qwindowsmenu.h b/src/plugins/platforms/windows/qwindowsmenu.h index aa36846ec0..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 @@ -44,8 +8,7 @@ #include <qpa/qplatformmenu.h> -#include <QtCore/qvector.h> -#include <QtCore/qpair.h> +#include <QtCore/qlist.h> QT_BEGIN_NAMESPACE @@ -120,7 +83,7 @@ class QWindowsMenu : public QPlatformMenu { Q_OBJECT public: - using MenuItems = QVector<QWindowsMenuItem *>; + using MenuItems = QList<QWindowsMenuItem *>; QWindowsMenu(); ~QWindowsMenu(); @@ -196,7 +159,7 @@ class QWindowsMenuBar : public QPlatformMenuBar { Q_OBJECT public: - using Menus = QVector<QWindowsMenu *>; + using Menus = QList<QWindowsMenu *>; QWindowsMenuBar(); ~QWindowsMenuBar() override; diff --git a/src/plugins/platforms/windows/qwindowsmime.h b/src/plugins/platforms/windows/qwindowsmime.h deleted file mode 100644 index f8708f1259..0000000000 --- a/src/plugins/platforms/windows/qwindowsmime.h +++ /dev/null @@ -1,108 +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 <QtCore/qt_windows.h> - -#include <QtCore/qvector.h> -#include <QtCore/qvariant.h> - -QT_BEGIN_NAMESPACE - -class QDebug; -class QMimeData; - -class QWindowsMime -{ - Q_DISABLE_COPY_MOVE(QWindowsMime) -public: - QWindowsMime(); - virtual ~QWindowsMime(); - - // for converting from Qt - virtual bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const = 0; - virtual bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const = 0; - virtual QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const = 0; - - // for converting to Qt - virtual bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const = 0; - virtual QVariant convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const = 0; - virtual QString mimeForFormat(const FORMATETC &formatetc) const = 0; - - static int registerMimeType(const QString &mime); -}; - -class QWindowsMimeConverter -{ - Q_DISABLE_COPY_MOVE(QWindowsMimeConverter) -public: - QWindowsMimeConverter(); - ~QWindowsMimeConverter(); - - QWindowsMime *converterToMime(const QString &mimeType, IDataObject *pDataObj) const; - QStringList allMimesForFormats(IDataObject *pDataObj) const; - QWindowsMime *converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; - QVector<FORMATETC> allFormatsForMime(const QMimeData *mimeData) const; - - // Convenience. - QVariant convertToMime(const QStringList &mimeTypes, IDataObject *pDataObj, QVariant::Type preferredType, - QString *format = nullptr) const; - - void registerMime(QWindowsMime *mime); - void unregisterMime(QWindowsMime *mime) { m_mimes.removeOne(mime); } - - static QString clipboardFormatName(int cf); - -private: - void ensureInitialized() const; - - mutable QVector<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 1c6c999f05..8d147e8fa0 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmimeregistry.cpp @@ -1,48 +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$ -** -****************************************************************************/ - -#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> #include <QtCore/qbytearraymatcher.h> -#include <QtCore/qtextcodec.h> #include <QtCore/qmap.h> #include <QtCore/qurl.h> #include <QtCore/qdir.h> @@ -56,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 @@ -97,7 +62,6 @@ struct BMP_BITMAPV5HEADER { DWORD bV5ProfileSize; DWORD bV5Reserved; }; -static const int BMP_BITFIELDS = 3; static const char dibFormatC[] = "dib"; @@ -115,16 +79,15 @@ static inline QByteArray msgConversionError(const char *func, const char *format return msg; } -static inline QImage readDib(QByteArray data) +static inline bool readDib(QBuffer &buffer, QImage &img) { - QBuffer buffer(&data); - buffer.open(QIODevice::ReadOnly); QImageReader reader(&buffer, dibFormatC); if (!reader.canRead()) { - qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData()); - return QImage(); + qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData()); + return false; } - return reader.read(); + img = reader.read(); + return true; } static QByteArray writeDib(const QImage &img) @@ -149,7 +112,10 @@ static bool qt_write_dibv5(QDataStream &s, QImage image) return false; //depth will be always 32 - int bpl_bmp = image.width()*4; + qsizetype bpl_bmp = qsizetype(image.width()) * 4; + qsizetype size = bpl_bmp * image.height(); + if (qsizetype(DWORD(size)) != size) + return false; BMP_BITMAPV5HEADER bi; ZeroMemory(&bi, sizeof(bi)); @@ -175,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]; @@ -211,94 +189,6 @@ static bool qt_write_dibv5(QDataStream &s, QImage image) return true; } -static int calc_shift(int mask) -{ - int result = 0; - while (!(mask & 1)) { - result++; - mask >>= 1; - } - return result; -} - -//Supports only 32 bit DIBV5 -static bool qt_read_dibv5(QDataStream &s, QImage &image) -{ - BMP_BITMAPV5HEADER bi; - QIODevice* d = s.device(); - if (d->atEnd()) - return false; - - d->read(reinterpret_cast<char *>(&bi), sizeof(bi)); // read BITMAPV5HEADER header - if (s.status() != QDataStream::Ok) - return false; - - const int nbits = bi.bV5BitCount; - if (nbits != 32 || bi.bV5Planes != 1 || bi.bV5Compression != BMP_BITFIELDS) - return false; //Unsupported DIBV5 format - - const int w = bi.bV5Width; - int h = bi.bV5Height; - const int red_mask = int(bi.bV5RedMask); - const int green_mask = int(bi.bV5GreenMask); - const int blue_mask = int(bi.bV5BlueMask); - const int alpha_mask = int(bi.bV5AlphaMask); - - const QImage::Format format = QImage::Format_ARGB32; - - if (bi.bV5Height < 0) - h = -h; // support images with negative height - if (image.size() != QSize(w, h) || image.format() != format) { - image = QImage(w, h, format); - if (image.isNull()) // could not create image - return false; - } - image.setDotsPerMeterX(bi.bV5XPelsPerMeter); - image.setDotsPerMeterY(bi.bV5YPelsPerMeter); - - const int red_shift = calc_shift(red_mask); - const int green_shift = calc_shift(green_mask); - const int blue_shift = calc_shift(blue_mask); - const int alpha_shift = alpha_mask ? calc_shift(alpha_mask) : 0u; - - const int bpl = image.bytesPerLine(); - uchar *data = image.bits(); - - auto *buf24 = new uchar[bpl]; - const int bpl24 = ((w * nbits + 31) / 32) * 4; - - while (--h >= 0) { - QRgb *p = reinterpret_cast<QRgb *>(data + h * bpl); - QRgb *end = p + w; - if (d->read(reinterpret_cast<char *>(buf24), bpl24) != bpl24) - break; - const uchar *b = buf24; - while (p < end) { - const int c = *b | (*(b + 1)) << 8 | (*(b + 2)) << 16 | (*(b + 3)) << 24; - *p++ = qRgba(((c & red_mask) >> red_shift) , - ((c & green_mask) >> green_shift), - ((c & blue_mask) >> blue_shift), - ((c & alpha_mask) >> alpha_shift)); - b += 4; - } - } - delete[] buf24; - - if (bi.bV5Height < 0) { - // Flip the image - auto *buf = new uchar[bpl]; - h = -bi.bV5Height; - for (int y = 0; y < h/2; ++y) { - memcpy(buf, data + y * bpl, size_t(bpl)); - memcpy(data + y*bpl, data + (h - y -1) * bpl, size_t(bpl)); - memcpy(data + (h - y -1 ) * bpl, buf, size_t(bpl)); - } - delete [] buf; - } - - return true; -} - // helpers for using global memory static int getCf(const FORMATETC &formatetc) @@ -410,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 @@ -443,145 +333,21 @@ 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 - \ingroup qt-lighthouse-win - - 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 -*/ - -/*! -Constructs a new conversion object, adding it to the globally accessed -list of available converters. -*/ -QWindowsMime::QWindowsMime() = default; - -/*! -Destroys a conversion object, removing it from the global -list of available converters. -*/ -QWindowsMime::~QWindowsMime() = default; - -/*! - Registers the MIME type \a mime, and returns an ID number - identifying the format on Windows. -*/ -int QWindowsMime::registerMimeType(const QString &mime) -{ - const UINT f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16())); - if (!f) - qErrnoWarning("QWindowsMime::registerMimeType: Failed to register clipboard format"); - - return int(f); -} - -/*! -\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 QVector<FORMATETC> QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const - - Returns a QVector 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, - QVariant::Type 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 QWindowsMime +class QWindowsMimeText : public QWindowsMimeConverter { public: bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override; - QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const override; + QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QMetaType preferredType) const override; QString mimeForFormat(const FORMATETC &formatetc) const override; bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override; bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override; - QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; + QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; }; 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(); } /* @@ -635,11 +401,11 @@ bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeDa int ri = 0; bool cr = false; for (int i=0; i < s; ++i) { - if (*u == QLatin1Char('\r')) + if (*u == u'\r') cr = true; else { - if (*u == QLatin1Char('\n') && !cr) - res[ri++] = QLatin1Char('\r'); + if (*u == u'\n' && !cr) + res[ri++] = u'\r'; cr = false; } res[ri++] = *u; @@ -663,7 +429,7 @@ bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeDa bool QWindowsMimeText::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const { - return mimeType.startsWith(QLatin1String("text/plain")) + return mimeType.startsWith(u"text/plain") && (canGetData(CF_UNICODETEXT, pDataObj) || canGetData(CF_TEXT, pDataObj)); } @@ -672,22 +438,23 @@ 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(); } -QVector<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +QList<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const { - QVector<FORMATETC> formatics; - if (mimeType.startsWith(QLatin1String("text/plain")) && mimeData->hasText()) { + 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; } -QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const +QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QMetaType preferredType) const { QVariant ret; @@ -696,12 +463,12 @@ 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()) { const char* d = data.data(); - const unsigned s = qstrlen(d); + const unsigned s = unsigned(qstrlen(d)); QByteArray r(data.size()+1, '\0'); char* o = r.data(); int j=0; @@ -714,7 +481,7 @@ QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pData str = QString::fromLocal8Bit(r); } } - if (preferredType == QVariant::String) + if (preferredType.id() == QMetaType::QString) ret = str; else ret = std::move(str).toUtf8(); @@ -723,16 +490,16 @@ QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pData return ret; } -class QWindowsMimeURI : public QWindowsMime +class QWindowsMimeURI : public QWindowsMimeConverter { public: QWindowsMimeURI(); bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override; - QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const override; + QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QMetaType preferredType) const override; QString mimeForFormat(const FORMATETC &formatetc) const override; bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override; bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override; - QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; + QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; private: int CF_INETURL_W; // wide char version int CF_INETURL; @@ -740,8 +507,8 @@ private: QWindowsMimeURI::QWindowsMimeURI() { - CF_INETURL_W = QWindowsMime::registerMimeType(QStringLiteral("UniformResourceLocatorW")); - CF_INETURL = QWindowsMime::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 @@ -762,7 +529,7 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat if (getCf(formatetc) == CF_HDROP) { const auto &urls = mimeData->urls(); QStringList fileNames; - int size = sizeof(DROPFILES)+2; + size_t size = sizeof(DROPFILES) + 2; for (const QUrl &url : urls) { const QString fn = QDir::toNativeSeparators(url.toLocalFile()); if (!fn.isEmpty()) { @@ -771,7 +538,7 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat } } - QByteArray result(size, '\0'); + QByteArray result(int(size), '\0'); auto* d = reinterpret_cast<DROPFILES *>(result.data()); d->pFiles = sizeof(DROPFILES); GetCursorPos(&d->pt); // try @@ -782,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; } @@ -794,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'); @@ -816,7 +583,7 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const { - return mimeType == QLatin1String("text/uri-list") + return mimeType == u"text/uri-list" && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj)); } @@ -824,14 +591,14 @@ 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; } -QVector<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +QList<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const { - QVector<FORMATETC> formatics; - if (mimeType == QLatin1String("text/uri-list")) { + QList<FORMATETC> formatics; + if (mimeType == u"text/uri-list") { if (canConvertFromMime(setCf(CF_HDROP), mimeData)) formatics += setCf(CF_HDROP); if (canConvertFromMime(setCf(CF_INETURL_W), mimeData)) @@ -842,9 +609,9 @@ QVector<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, cons return formatics; } -QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const +QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QMetaType preferredType) const { - if (mimeType == QLatin1String("text/uri-list")) { + if (mimeType == u"text/uri-list") { if (canGetData(CF_HDROP, pDataObj)) { QList<QVariant> urls; @@ -870,7 +637,7 @@ QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pD } } - if (preferredType == QVariant::Url && urls.size() == 1) + if (preferredType.id() == QMetaType::QUrl && urls.size() == 1) return urls.at(0); if (!urls.isEmpty()) return urls; @@ -889,7 +656,7 @@ QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pD return QVariant(); } -class QWindowsMimeHtml : public QWindowsMime +class QWindowsMimeHtml : public QWindowsMimeConverter { public: QWindowsMimeHtml(); @@ -897,11 +664,11 @@ public: // for converting from Qt bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override; bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override; - QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; + QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; // for converting to Qt bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override; - QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const override; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override; QString mimeForFormat(const FORMATETC &formatetc) const override; private: @@ -910,13 +677,13 @@ private: QWindowsMimeHtml::QWindowsMimeHtml() { - CF_HTML = QWindowsMime::registerMimeType(QStringLiteral("HTML Format")); + CF_HTML = registerMimeType(u"HTML Format"_s); } -QVector<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +QList<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const { - QVector<FORMATETC> formatetcs; - if (mimeType == QLatin1String("text/html") && (!mimeData->html().isEmpty())) + QList<FORMATETC> formatetcs; + if (mimeType == u"text/html" && (!mimeData->html().isEmpty())) formatetcs += setCf(CF_HTML); return formatetcs; } @@ -924,13 +691,13 @@ QVector<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, con QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const { if (getCf(formatetc) == CF_HTML) - return QStringLiteral("text/html"); + return u"text/html"_s; return QString(); } bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const { - return mimeType == QLatin1String("text/html") && canGetData(CF_HTML, pDataObj); + return mimeType == u"text/html" && canGetData(CF_HTML, pDataObj); } @@ -951,14 +718,14 @@ in bytes). Charset used is mostly utf8, but can be different, ie. we have to loo ...html... */ -QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const +QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const { Q_UNUSED(preferredType); QVariant result; if (canConvertToMime(mime, pDataObj)) { QByteArray html = getData(CF_HTML, pDataObj); - static Q_RELAXED_CONSTEXPR auto startMatcher = qMakeStaticByteArrayMatcher("StartHTML:"); - static Q_RELAXED_CONSTEXPR auto endMatcher = qMakeStaticByteArrayMatcher("EndHTML:"); + static constexpr auto startMatcher = qMakeStaticByteArrayMatcher("StartHTML:"); + static constexpr auto endMatcher = qMakeStaticByteArrayMatcher("EndHTML:"); qCDebug(lcQpaMime) << __FUNCTION__ << "raw:" << html; int start = startMatcher.indexIn(html); int end = endMatcher.indexIn(html); @@ -1001,8 +768,8 @@ bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeDa "StartFragment:0000000000\r\n" // 56-81 "EndFragment:0000000000\r\n\r\n"; // 82-107 - static Q_RELAXED_CONSTEXPR auto startFragmentMatcher = qMakeStaticByteArrayMatcher("<!--StartFragment-->"); - static Q_RELAXED_CONSTEXPR auto endFragmentMatcher = qMakeStaticByteArrayMatcher("<!--EndFragment-->"); + static constexpr auto startFragmentMatcher = qMakeStaticByteArrayMatcher("<!--StartFragment-->"); + static constexpr auto endFragmentMatcher = qMakeStaticByteArrayMatcher("<!--EndFragment-->"); if (startFragmentMatcher.indexIn(data) == -1) result += "<!--StartFragment-->"; @@ -1027,18 +794,18 @@ bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeDa #ifndef QT_NO_IMAGEFORMAT_BMP -class QWindowsMimeImage : public QWindowsMime +class QWindowsMimeImage : public QWindowsMimeConverter { public: QWindowsMimeImage(); // for converting from Qt bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override; bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override; - QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; + QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; // for converting to Qt bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override; - QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const override; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override; QString mimeForFormat(const FORMATETC &formatetc) const override; private: bool hasOriginalDIBV5(IDataObject *pDataObj) const; @@ -1050,10 +817,10 @@ QWindowsMimeImage::QWindowsMimeImage() CF_PNG = RegisterClipboardFormat(L"PNG"); } -QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +QList<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const { - QVector<FORMATETC> formatetcs; - if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) { + QList<FORMATETC> formatetcs; + if (mimeData->hasImage() && mimeType == u"application/x-qt-image") { //add DIBV5 if image has alpha channel. Do not add CF_PNG here as it will confuse MS Office (QTBUG47656). auto image = qvariant_cast<QImage>(mimeData->imageData()); if (!image.isNull() && image.hasAlphaChannel()) @@ -1069,13 +836,13 @@ 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(); } bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const { - return mimeType == QLatin1String("application/x-qt-image") + return mimeType == u"application/x-qt-image" && (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj)); } @@ -1103,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); @@ -1116,7 +883,7 @@ 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); } } @@ -1136,7 +903,7 @@ bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const if (fc.cfFormat == CF_DIB) break; if (fc.cfFormat == CF_DIBV5) { - isSynthesized = false; + isSynthesized = false; break; } } @@ -1145,25 +912,28 @@ bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const return !isSynthesized; } -QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const { Q_UNUSED(preferredType); QVariant result; - if (mimeType != QLatin1String("application/x-qt-image")) + if (mimeType != u"application/x-qt-image") return result; - //Try to convert from a format which has more data - //DIBV5, use only if its is not synthesized - if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(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); - QDataStream s(&data, QIODevice::ReadOnly); - s.setByteOrder(QDataStream::LittleEndian); - if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5 + QBuffer buffer(&data); + if (readDib(buffer, img)) return img; - } } //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")) { @@ -1172,8 +942,11 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject * } //Fallback to DIB if (canGetData(CF_DIB, pDataObj)) { - const QImage img = readDib(getData(CF_DIB, pDataObj)); - if (!img.isNull()) + qCDebug(lcQpaMime) << "Decoding DIB"; + QImage img; + QByteArray data = getData(CF_DIBV5, pDataObj); + QBuffer buffer(&data); + if (readDib(buffer, img)) return img; } // Failed @@ -1181,7 +954,7 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject * } #endif -class QBuiltInMimes : public QWindowsMime +class QBuiltInMimes : public QWindowsMimeConverter { public: QBuiltInMimes(); @@ -1189,11 +962,11 @@ public: // for converting from Qt bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override; bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override; - QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; + QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; // for converting to Qt bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override; - QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const override; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override; QString mimeForFormat(const FORMATETC &formatetc) const override; private: @@ -1202,10 +975,10 @@ private: }; QBuiltInMimes::QBuiltInMimes() -: QWindowsMime() +: QWindowsMimeConverter() { - outFormats.insert(QWindowsMime::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color")); - inFormats.insert(QWindowsMime::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 @@ -1220,7 +993,7 @@ bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData { if (canConvertFromMime(formatetc, mimeData)) { QByteArray data; - if (outFormats.value(getCf(formatetc)) == QLatin1String("text/html")) { + if (outFormats.value(getCf(formatetc)) == u"text/html") { // text/html is in wide chars on windows (compatible with mozillia) QString html = mimeData->html(); // same code as in the text converter up above @@ -1232,11 +1005,11 @@ bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData int ri = 0; bool cr = false; for (int i=0; i < s; ++i) { - if (*u == QLatin1Char('\r')) + if (*u == u'\r') cr = true; else { - if (*u == QLatin1Char('\n') && !cr) - res[ri++] = QLatin1Char('\r'); + if (*u == u'\n' && !cr) + res[ri++] = u'\r'; cr = false; } res[ri++] = *u; @@ -1263,9 +1036,9 @@ bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData return false; } -QVector<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +QList<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const { - QVector<FORMATETC> formatetcs; + QList<FORMATETC> formatetcs; const auto mit = std::find(outFormats.cbegin(), outFormats.cend(), mimeType); if (mit != outFormats.cend() && mimeData->formats().contains(mimeType)) formatetcs += setCf(mit.key()); @@ -1278,14 +1051,14 @@ bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pData return mit != inFormats.cend() && canGetData(mit.key(), pDataObj); } -QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const { QVariant val; if (canConvertToMime(mimeType, pDataObj)) { QByteArray data = getData(inFormats.key(mimeType), pDataObj); if (!data.isEmpty()) { qCDebug(lcQpaMime) << __FUNCTION__; - if (mimeType == QLatin1String("text/html") && preferredType == QVariant::String) { + if (mimeType == u"text/html" && preferredType == QMetaType(QMetaType::QString)) { // text/html is in wide chars on windows (compatible with Mozilla) val = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData())); } else { @@ -1302,7 +1075,7 @@ QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const } -class QLastResortMimes : public QWindowsMime +class QLastResortMimes : public QWindowsMimeConverter { public: @@ -1310,11 +1083,11 @@ public: // for converting from Qt bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override; bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override; - QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; + QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override; // for converting to Qt bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override; - QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const override; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override; QString mimeForFormat(const FORMATETC &formatetc) const override; private: @@ -1330,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); } } @@ -1380,13 +1153,13 @@ bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeDa #endif // QT_CONFIG(draganddrop) } -QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const +QList<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const { - QVector<FORMATETC> formatetcs; + QList<FORMATETC> formatetcs; 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(QWindowsMime::registerMimeType(mimeType), mimeType); + mit = formats.insert(registerMimeType(mimeType), mimeType); if (mit != formats.end()) formatetcs += setCf(mit.key()); @@ -1398,21 +1171,21 @@ 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) { int len = sizeof(x_qt_windows_mime) - 1; - int n = mimeType.lastIndexOf(QLatin1Char('\"')) - len; + int n = mimeType.lastIndexOf(u'\"') - len; QString ret = mimeType.mid(len, n); - const int beginPos = mimeType.indexOf(QLatin1String(";index=")); + const int beginPos = mimeType.indexOf(u";index="); if (beginPos > -1) { - const int endPos = mimeType.indexOf(QLatin1Char(';'), beginPos + 1); + const int endPos = mimeType.indexOf(u';', beginPos + 1); const int indexStartPos = beginPos + 7; if (lindex) - *lindex = mimeType.midRef(indexStartPos, endPos == -1 ? endPos : endPos - indexStartPos).toInt(); + *lindex = QStringView{mimeType}.mid(indexStartPos, endPos == -1 ? endPos : endPos - indexStartPos).toInt(); } else { if (lindex) *lindex = -1; @@ -1430,11 +1203,11 @@ 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() : QWindowsMime::registerMimeType(mimeType); + const int cf = mit != formats.cend() ? mit.key() : registerMimeType(mimeType); return canGetData(cf, pDataObj); } -QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const { Q_UNUSED(preferredType); QVariant val; @@ -1447,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() : QWindowsMime::registerMimeType(mimeType); + const int cf = mit != formats.cend() ? mit.key() : registerMimeType(mimeType); data = getData(cf, pDataObj); } if (!data.isEmpty()) @@ -1462,12 +1235,12 @@ 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)) format = clipFormat; - else if((formatetc.cfFormat >= 0xC000)){ + else if ((formatetc.cfFormat >= 0xC000)){ //create the mime as custom. not registered. if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) { //check if this is a mime type @@ -1480,7 +1253,7 @@ QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const } } if (!ianaType) - format = QLatin1String(x_qt_windows_mime) + clipFormat + QLatin1Char('\"'); + format = QLatin1StringView(x_qt_windows_mime) + clipFormat + u'"'; else format = clipFormat; } @@ -1492,21 +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 - \ingroup qt-lighthouse-win - \sa QWindowsMime + \sa QWindowsMimeConverter */ -QWindowsMimeConverter::QWindowsMimeConverter() = default; +QWindowsMimeRegistry::QWindowsMimeRegistry() = default; -QWindowsMimeConverter::~QWindowsMimeConverter() +QWindowsMimeRegistry::~QWindowsMimeRegistry() { qDeleteAll(m_mimes.begin(), m_mimes.begin() + m_internalMimeCount); } -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) { @@ -1516,9 +1288,9 @@ QWindowsMime * QWindowsMimeConverter::converterToMime(const QString &mimeType, I 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; @@ -1545,7 +1317,7 @@ QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) con return formats; } -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; @@ -1556,10 +1328,10 @@ QWindowsMime * QWindowsMimeConverter::converterFromMime(const FORMATETC &formate return nullptr; } -QVector<FORMATETC> QWindowsMimeConverter::allFormatsForMime(const QMimeData *mimeData) const +QList<FORMATETC> QWindowsMimeRegistry::allFormatsForMime(const QMimeData *mimeData) const { ensureInitialized(); - QVector<FORMATETC> formatics; + QList<FORMATETC> formatics; #if !QT_CONFIG(draganddrop) Q_UNUSED(mimeData); #else @@ -1573,34 +1345,37 @@ QVector<FORMATETC> QWindowsMimeConverter::allFormatsForMime(const QMimeData *mim 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, - QVariant::Type preferredType, + 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()) { @@ -1613,14 +1388,33 @@ QVariant QWindowsMimeConverter::convertToMime(const QStringList &mimeTypes, } } } - qCDebug(lcQpaMime) << __FUNCTION__ << "fails" << mimeTypes << pDataObj << preferredType; + qCDebug(lcQpaMime) << __FUNCTION__ << "fails" << mimeTypes << pDataObj << preferredType.id(); return QVariant(); } -void QWindowsMimeConverter::registerMime(QWindowsMime *mime) +void QWindowsMimeRegistry::registerMime(QWindowsMimeConverter *mime) { ensureInitialized(); m_mimes.append(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 QWindowsMimeRegistry::registerMimeType(const QString &mime) +{ + 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); +} + QT_END_NAMESPACE 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 b776efc942..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" @@ -47,12 +11,13 @@ #include <qpa/qwindowsysteminterface.h> #include <QtGui/qguiapplication.h> #include <QtGui/qscreen.h> -#include <QtGui/qtouchdevice.h> +#include <QtGui/qpointingdevice.h> #include <QtGui/qwindow.h> #include <QtGui/qcursor.h> #include <QtCore/qdebug.h> -#include <QtCore/qscopedpointer.h> + +#include <memory> #include <windowsx.h> @@ -117,27 +82,6 @@ static inline void compressMouseMove(MSG *msg) } } -static inline QTouchDevice *createTouchDevice() -{ - const int digitizers = GetSystemMetrics(SM_DIGITIZER); - if (!(digitizers & (NID_INTEGRATED_TOUCH | NID_EXTERNAL_TOUCH))) - return nullptr; - const int tabletPc = GetSystemMetrics(SM_TABLETPC); - const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES); - qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << (digitizers & ~NID_READY) - << "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase - << "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints; - auto *result = new QTouchDevice; - result->setType(digitizers & NID_INTEGRATED_TOUCH - ? QTouchDevice::TouchScreen : QTouchDevice::TouchPad); - QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition; - if (result->type() == QTouchDevice::TouchPad) - capabilities |= QTouchDevice::MouseEmulation; - result->setCapabilities(capabilities); - result->setMaximumTouchPoints(maxTouchPoints); - return result; -} - /*! \class QWindowsMouseHandler \brief Windows mouse handler @@ -145,16 +89,16 @@ static inline QTouchDevice *createTouchDevice() Dispatches mouse and touch events. Separate for code cleanliness. \internal - \ingroup qt-lighthouse-win */ QWindowsMouseHandler::QWindowsMouseHandler() = default; -QTouchDevice *QWindowsMouseHandler::ensureTouchDevice() +const QPointingDevice *QWindowsMouseHandler::primaryMouse() { - if (!m_touchDevice) - m_touchDevice = createTouchDevice(); - return m_touchDevice; + static QPointer<const QPointingDevice> result; + if (!result) + result = QPointingDevice::primaryPointingDevice(); + return result; } void QWindowsMouseHandler::clearEvents() @@ -172,7 +116,7 @@ Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons() if (GetAsyncKeyState(VK_RBUTTON) < 0) result |= mouseSwapped ? Qt::LeftButton : Qt::RightButton; if (GetAsyncKeyState(VK_MBUTTON) < 0) - result |= Qt::MidButton; + result |= Qt::MiddleButton; if (GetAsyncKeyState(VK_XBUTTON1) < 0) result |= Qt::XButton1; if (GetAsyncKeyState(VK_XBUTTON2) < 0) @@ -180,7 +124,7 @@ Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons() return result; } -static QPoint lastMouseMovePos; +Q_CONSTINIT static QPoint lastMouseMovePos; namespace { struct MouseEvent { @@ -216,11 +160,11 @@ static inline MouseEvent eventFromMsg(const MSG &msg) case WM_LBUTTONDBLCLK: // Qt QPA does not handle double clicks, send as press return {QEvent::MouseButtonPress, Qt::LeftButton}; case WM_MBUTTONDOWN: - return {QEvent::MouseButtonPress, Qt::MidButton}; + return {QEvent::MouseButtonPress, Qt::MiddleButton}; case WM_MBUTTONUP: - return {QEvent::MouseButtonRelease, Qt::MidButton}; + return {QEvent::MouseButtonRelease, Qt::MiddleButton}; case WM_MBUTTONDBLCLK: - return {QEvent::MouseButtonPress, Qt::MidButton}; + return {QEvent::MouseButtonPress, Qt::MiddleButton}; case WM_RBUTTONDOWN: return {QEvent::MouseButtonPress, Qt::RightButton}; case WM_RBUTTONUP: @@ -242,11 +186,11 @@ static inline MouseEvent eventFromMsg(const MSG &msg) case WM_NCLBUTTONDBLCLK: return {QEvent::NonClientAreaMouseButtonPress, Qt::LeftButton}; case WM_NCMBUTTONDOWN: - return {QEvent::NonClientAreaMouseButtonPress, Qt::MidButton}; + return {QEvent::NonClientAreaMouseButtonPress, Qt::MiddleButton}; case WM_NCMBUTTONUP: - return {QEvent::NonClientAreaMouseButtonRelease, Qt::MidButton}; + return {QEvent::NonClientAreaMouseButtonRelease, Qt::MiddleButton}; case WM_NCMBUTTONDBLCLK: - return {QEvent::NonClientAreaMouseButtonPress, Qt::MidButton}; + return {QEvent::NonClientAreaMouseButtonPress, Qt::MiddleButton}; case WM_NCRBUTTONDOWN: return {QEvent::NonClientAreaMouseButtonPress, Qt::RightButton}; case WM_NCRBUTTONUP: @@ -281,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" @@ -299,6 +248,8 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; + const QPointingDevice *device = primaryMouse(); + // Check for events synthesized from touch. Lower byte is touch index, 0 means pen. static const bool passSynthesizedMouseEvents = !(QWindowsIntegration::instance()->options() & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch); @@ -310,12 +261,15 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, if ((extraInfo & signatureMask) == miWpSignature) { if (extraInfo & 0x80) { // Bit 7 indicates touch event, else tablet pen. source = Qt::MouseEventSynthesizedBySystem; + if (!m_touchDevice.isNull()) + device = m_touchDevice.data(); if (!passSynthesizedMouseEvents) return false; } } - 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; @@ -333,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, clientPosition, globalPosition, buttons, m_lastEventButton, - QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source); - } else { - QWindowSystemInterface::handleMouseEvent(window, 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, clientPosition, + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, clientPosition, globalPosition, buttons, mouseEvent.button, mouseEvent.type, keyModifiers, source); @@ -372,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. @@ -495,7 +446,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } if (!discardEvent && mouseEvent.type != QEvent::None) { - QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons, + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, clientPosition, globalPosition, buttons, mouseEvent.button, mouseEvent.type, keyModifiers, source); } @@ -519,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: @@ -540,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); } @@ -568,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; } @@ -599,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; } @@ -625,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); - Qt::TouchPointStates allStates; + 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); @@ -657,15 +608,15 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, touchPoint.normalPosition = normalPosition; if (winTouchInput.dwFlags & TOUCHEVENTF_DOWN) { - touchPoint.state = Qt::TouchPointPressed; + touchPoint.state = QEventPoint::State::Pressed; m_lastTouchPositions.insert(id, touchPoint.normalPosition); } else if (winTouchInput.dwFlags & TOUCHEVENTF_UP) { - touchPoint.state = Qt::TouchPointReleased; + touchPoint.state = QEventPoint::State::Released; m_lastTouchPositions.remove(id); } else { touchPoint.state = (stationaryTouchPoint - ? Qt::TouchPointStationary - : Qt::TouchPointMoved); + ? QEventPoint::State::Stationary + : QEventPoint::State::Updated); m_lastTouchPositions.insert(id, touchPoint.normalPosition); } @@ -677,13 +628,15 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(msg.lParam)); // all touch points released, forget the ids we've seen, they may not be reused - if (allStates == Qt::TouchPointReleased) + if (allStates == QEventPoint::State::Released) m_touchInputIDToTouchPointID.clear(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); QWindowSystemInterface::handleTouchEvent(window, - m_touchDevice, + msg.time, + m_touchDevice.data(), touchPoints, - QWindowsKeyMapper::queryKeyboardModifiers()); + keyMapper->queryKeyboardModifiers()); return true; } @@ -691,9 +644,9 @@ bool QWindowsMouseHandler::translateGestureEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType, MSG msg, LRESULT *) { - Q_UNUSED(window) - Q_UNUSED(hwnd) - Q_UNUSED(msg) + Q_UNUSED(window); + Q_UNUSED(hwnd); + Q_UNUSED(msg); return false; } diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.h b/src/plugins/platforms/windows/qwindowsmousehandler.h index 1d3d1f4761..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 @@ -45,21 +9,24 @@ #include <QtCore/qpointer.h> #include <QtCore/qhash.h> +#include <QtCore/qsharedpointer.h> #include <QtGui/qevent.h> QT_BEGIN_NAMESPACE class QWindow; -class QTouchDevice; +class QPointingDevice; class QWindowsMouseHandler { Q_DISABLE_COPY_MOVE(QWindowsMouseHandler) public: + using QPointingDevicePtr = QSharedPointer<QPointingDevice>; + QWindowsMouseHandler(); - QTouchDevice *touchDevice() const { return m_touchDevice; } - QTouchDevice *ensureTouchDevice(); + const QPointingDevicePtr &touchDevice() const { return m_touchDevice; } + void setTouchDevice(const QPointingDevicePtr &d) { m_touchDevice = d; } bool translateMouseEvent(QWindow *widget, HWND hwnd, QtWindows::WindowsEventType t, MSG msg, @@ -82,6 +49,8 @@ public: void clearWindowUnderMouse() { m_windowUnderMouse = nullptr; } void clearEvents(); + static const QPointingDevice *primaryMouse(); + private: inline bool translateMouseWheelEvent(QWindow *window, HWND hwnd, MSG msg, LRESULT *result); @@ -90,7 +59,7 @@ private: QPointer<QWindow> m_trackedWindow; QHash<DWORD, int> m_touchInputIDToTouchPointID; QHash<int, QPointF> m_lastTouchPositions; - QTouchDevice *m_touchDevice = nullptr; + QPointingDevicePtr m_touchDevice; bool m_leftButtonDown = false; QWindow *m_previousCaptureWindow = nullptr; QEvent::Type m_lastEventType = QEvent::None; diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp index d1d181d66e..e709123097 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp @@ -1,67 +1,25 @@ -/**************************************************************************** -** -** 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 "qwindowsclipboard.h" #include "qwindowswindow.h" #include "qwindowsscreen.h" #include "qwindowscontext.h" #include "qwindowscursor.h" #include "qwindowsopenglcontext.h" -#include "qwindowsopengltester.h" #include "qwindowsintegration.h" -#include "qwindowsmime.h" +#include "qwindowstheme.h" #include "qwin10helpers.h" #include <QtGui/qwindow.h> #include <QtGui/qopenglcontext.h> #include <QtGui/qscreen.h> #include <qpa/qplatformscreen.h> -#include <QtFontDatabaseSupport/private/qwindowsfontdatabase_p.h> QT_BEGIN_NAMESPACE enum ResourceType { RenderingContextType, - EglContextType, - EglDisplayType, - EglConfigType, HandleType, GlHandleType, GetDCType, @@ -73,9 +31,6 @@ static int resourceType(const QByteArray &key) { static const char *names[] = { // match ResourceType "renderingcontext", - "eglcontext", - "egldisplay", - "eglconfig", "handle", "glhandle", "getdc", @@ -89,9 +44,6 @@ static int resourceType(const QByteArray &key) return int(result - names); } -QWindowsWindowFunctions::WindowActivationBehavior QWindowsNativeInterface::m_windowActivationBehavior = - QWindowsWindowFunctions::DefaultActivateWindow; - void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window) { if (!window || !window->handle()) { @@ -153,41 +105,10 @@ void *QWindowsNativeInterface::nativeResourceForCursor(const QByteArray &resourc } #endif // !QT_NO_CURSOR -static const char customMarginPropertyC[] = "WindowsCustomMargins"; - -QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const -{ - auto *platformWindow = static_cast<QWindowsWindow *>(window); - if (name == QLatin1String(customMarginPropertyC)) - return QVariant::fromValue(platformWindow->customMargins()); - return QVariant(); -} - -QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const -{ - const QVariant result = windowProperty(window, name); - return result.isValid() ? result : defaultValue; -} - -void QWindowsNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) -{ - auto *platformWindow = static_cast<QWindowsWindow *>(window); - if (name == QLatin1String(customMarginPropertyC)) - platformWindow->setCustomMargins(qvariant_cast<QMargins>(value)); -} - -QVariantMap QWindowsNativeInterface::windowProperties(QPlatformWindow *window) const -{ - QVariantMap result; - const QString customMarginProperty = QLatin1String(customMarginPropertyC); - result.insert(customMarginProperty, windowProperty(window, customMarginProperty)); - return result; -} - void *QWindowsNativeInterface::nativeResourceForIntegration(const QByteArray &resource) { #ifdef QT_NO_OPENGL - Q_UNUSED(resource) + Q_UNUSED(resource); #else if (resourceType(resource) == GlHandleType) { if (const QWindowsStaticOpenGLContext *sc = QWindowsIntegration::staticOpenGLContext()) @@ -206,114 +127,9 @@ void *QWindowsNativeInterface::nativeResourceForContext(const QByteArray &resour return nullptr; } - auto *glcontext = static_cast<QWindowsOpenGLContext *>(context->handle()); - switch (resourceType(resource)) { - case RenderingContextType: // Fall through. - case EglContextType: - return glcontext->nativeContext(); - case EglDisplayType: - return glcontext->nativeDisplay(); - case EglConfigType: - return glcontext->nativeConfig(); - default: - break; - } - qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData()); return nullptr; } #endif // !QT_NO_OPENGL -/*! - \brief Creates a non-visible window handle for filtering messages. -*/ - -void *QWindowsNativeInterface::createMessageWindow(const QString &classNameTemplate, - const QString &windowName, - void *eventProc) const -{ - QWindowsContext *ctx = QWindowsContext::instance(); - const HWND hwnd = ctx->createDummyWindow(classNameTemplate, - (wchar_t*)windowName.utf16(), - (WNDPROC)eventProc); - return hwnd; -} - -/*! - \brief Registers a unique window class with a callback function based on \a classNameIn. -*/ - -QString QWindowsNativeInterface::registerWindowClass(const QString &classNameIn, void *eventProc) const -{ - return QWindowsContext::instance()->registerWindowClass(classNameIn, (WNDPROC)eventProc); -} - -bool QWindowsNativeInterface::asyncExpose() const -{ - return QWindowsContext::instance()->asyncExpose(); -} - -void QWindowsNativeInterface::setAsyncExpose(bool value) -{ - QWindowsContext::instance()->setAsyncExpose(value); -} - -void QWindowsNativeInterface::registerWindowsMime(void *mimeIn) -{ - QWindowsContext::instance()->mimeConverter().registerMime(reinterpret_cast<QWindowsMime *>(mimeIn)); -} - -void QWindowsNativeInterface::unregisterWindowsMime(void *mimeIn) -{ - QWindowsContext::instance()->mimeConverter().unregisterMime(reinterpret_cast<QWindowsMime *>(mimeIn)); -} - -int QWindowsNativeInterface::registerMimeType(const QString &mimeType) -{ - return QWindowsMime::registerMimeType(mimeType); -} - -QFont QWindowsNativeInterface::logFontToQFont(const void *logFont, int verticalDpi) -{ - return QWindowsFontDatabase::LOGFONT_to_QFont(*reinterpret_cast<const LOGFONT *>(logFont), verticalDpi); -} - -bool QWindowsNativeInterface::isTabletMode() -{ -#if QT_CONFIG(clipboard) - if (const QWindowsClipboard *clipboard = QWindowsClipboard::instance()) - return qt_windowsIsTabletMode(clipboard->clipboardViewer()); -#endif - return false; -} - -QFunctionPointer QWindowsNativeInterface::platformFunction(const QByteArray &function) const -{ - if (function == QWindowsWindowFunctions::setTouchWindowTouchTypeIdentifier()) - return QFunctionPointer(QWindowsWindow::setTouchWindowTouchTypeStatic); - if (function == QWindowsWindowFunctions::setHasBorderInFullScreenIdentifier()) - return QFunctionPointer(QWindowsWindow::setHasBorderInFullScreenStatic); - if (function == QWindowsWindowFunctions::setHasBorderInFullScreenDefaultIdentifier()) - return QFunctionPointer(QWindowsWindow::setHasBorderInFullScreenDefault); - if (function == QWindowsWindowFunctions::setWindowActivationBehaviorIdentifier()) - return QFunctionPointer(QWindowsNativeInterface::setWindowActivationBehavior); - if (function == QWindowsWindowFunctions::isTabletModeIdentifier()) - return QFunctionPointer(QWindowsNativeInterface::isTabletMode); - return nullptr; -} - -QVariant QWindowsNativeInterface::gpu() const -{ - return GpuDescription::detect().toVariant(); -} - -QVariant QWindowsNativeInterface::gpuList() const -{ - QVariantList result; - const auto gpus = GpuDescription::detectAll(); - for (const auto &gpu : gpus) - result.append(gpu.toVariant()); - return result; -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.h b/src/plugins/platforms/windows/qwindowsnativeinterface.h index ce395dc5a4..a123c2b242 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.h +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.h @@ -1,48 +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 #ifndef QWINDOWSNATIVEINTERFACE_H #define QWINDOWSNATIVEINTERFACE_H #include <QtGui/qfont.h> #include <QtGui/qpa/qplatformnativeinterface.h> -#include <QtPlatformHeaders/qwindowswindowfunctions.h> QT_BEGIN_NAMESPACE @@ -58,15 +21,11 @@ QT_BEGIN_NAMESPACE \endlist \internal - \ingroup qt-lighthouse-win */ class QWindowsNativeInterface : public QPlatformNativeInterface { Q_OBJECT - Q_PROPERTY(bool asyncExpose READ asyncExpose WRITE setAsyncExpose) - Q_PROPERTY(QVariant gpu READ gpu STORED false) - Q_PROPERTY(QVariant gpuList READ gpuList STORED false) public: void *nativeResourceForIntegration(const QByteArray &resource) override; @@ -78,39 +37,6 @@ public: #ifndef QT_NO_CURSOR void *nativeResourceForCursor(const QByteArray &resource, const QCursor &cursor) override; #endif - Q_INVOKABLE void *createMessageWindow(const QString &classNameTemplate, - const QString &windowName, - void *eventProc) const; - - Q_INVOKABLE QString registerWindowClass(const QString &classNameIn, void *eventProc) const; - - Q_INVOKABLE void registerWindowsMime(void *mimeIn); - Q_INVOKABLE void unregisterWindowsMime(void *mime); - Q_INVOKABLE int registerMimeType(const QString &mimeType); - Q_INVOKABLE QFont logFontToQFont(const void *logFont, int verticalDpi); - - bool asyncExpose() const; - void setAsyncExpose(bool value); - - QVariant gpu() const; - QVariant gpuList() const; - - QVariantMap windowProperties(QPlatformWindow *window) const override; - QVariant windowProperty(QPlatformWindow *window, const QString &name) const override; - QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const override; - void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) override; - - static QWindowsWindowFunctions::WindowActivationBehavior windowActivationBehavior() - { return QWindowsNativeInterface::m_windowActivationBehavior; } - static void setWindowActivationBehavior(QWindowsWindowFunctions::WindowActivationBehavior b) - { QWindowsNativeInterface::m_windowActivationBehavior = b; } - - static bool isTabletMode(); - - QFunctionPointer platformFunction(const QByteArray &function) const override; - -private: - static QWindowsWindowFunctions::WindowActivationBehavior m_windowActivationBehavior; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp index f3450e2806..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> @@ -70,7 +34,6 @@ QT_BEGIN_NAMESPACE \endlist \internal - \ingroup qt-lighthouse-win */ QWindowsOleDataObject::QWindowsOleDataObject(QMimeData *mimeData) : @@ -103,8 +66,8 @@ QWindowsOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) HRESULT hr = ResultFromScode(DATA_E_FORMATETC); if (data) { - const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); - if (QWindowsMime *converter = mc.converterFromMime(*pformatetc, data)) + const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); + if (auto converter = mc.converterFromMime(*pformatetc, data)) if (converter->convertFromMime(*pformatetc, data, pmedium)) hr = ResultFromScode(S_OK); } @@ -130,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); } @@ -179,9 +142,9 @@ QWindowsOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppe SCODE sc = S_OK; - QVector<FORMATETC> fmtetcs; + QList<FORMATETC> fmtetcs; if (dwDirection == DATADIR_GET) { - QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter(); fmtetcs = mc.allFormatsForMime(data); } else { FORMATETC formatetc; @@ -228,10 +191,9 @@ QWindowsOleDataObject::EnumDAdvise(LPENUMSTATDATA FAR*) \class QWindowsOleEnumFmtEtc \brief Enumerates the FORMATETC structures supported by QWindowsOleDataObject. \internal - \ingroup qt-lighthouse-win */ -QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs) +QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QList<FORMATETC> &fmtetcs) { if (QWindowsContext::verbose > 1) qCDebug(lcQpaMime) << __FUNCTION__ << fmtetcs; @@ -248,7 +210,7 @@ QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs) } } -QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector<LPFORMATETC> &lpfmtetcs) +QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QList<LPFORMATETC> &lpfmtetcs) { if (QWindowsContext::verbose > 1) qCDebug(lcQpaMime) << __FUNCTION__; diff --git a/src/plugins/platforms/windows/qwindowsole.h b/src/plugins/platforms/windows/qwindowsole.h index 6940657e88..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/qvector.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); @@ -65,17 +29,17 @@ public: DWORD reportedPerformedEffect() const; // IDataObject methods - STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium); - STDMETHOD(GetDataHere)(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium); - STDMETHOD(QueryGetData)(LPFORMATETC pformatetc); - STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC pformatetc, LPFORMATETC pformatetcOut); - STDMETHOD(SetData)(LPFORMATETC pformatetc, STGMEDIUM FAR * pmedium, - BOOL fRelease); - STDMETHOD(EnumFormatEtc)(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc); - STDMETHOD(DAdvise)(FORMATETC FAR* pFormatetc, DWORD advf, - LPADVISESINK pAdvSink, DWORD FAR* pdwConnection); - STDMETHOD(DUnadvise)(DWORD dwConnection); - STDMETHOD(EnumDAdvise)(LPENUMSTATDATA FAR* ppenumAdvise); + STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium) override; + STDMETHOD(GetDataHere)(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) override; + STDMETHOD(QueryGetData)(LPFORMATETC pformatetc) override; + STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC pformatetc, LPFORMATETC pformatetcOut) override; + STDMETHOD(SetData)(LPFORMATETC pformatetc, STGMEDIUM FAR *pmedium, BOOL fRelease) override; + STDMETHOD(EnumFormatEtc)(DWORD dwDirection, LPENUMFORMATETC FAR *ppenumFormatEtc) override; + STDMETHOD(DAdvise) + (FORMATETC FAR *pFormatetc, DWORD advf, LPADVISESINK pAdvSink, + DWORD FAR *pdwConnection) override; + STDMETHOD(DUnadvise)(DWORD dwConnection) override; + STDMETHOD(EnumDAdvise)(LPENUMSTATDATA FAR *ppenumAdvise) override; private: QPointer<QMimeData> data; @@ -83,26 +47,26 @@ private: DWORD performedEffect = DROPEFFECT_NONE; }; -class QWindowsOleEnumFmtEtc : public QWindowsComBase<IEnumFORMATETC> +class QWindowsOleEnumFmtEtc : public QComObject<IEnumFORMATETC> { public: - explicit QWindowsOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs); - explicit QWindowsOleEnumFmtEtc(const QVector<LPFORMATETC> &lpfmtetcs); + explicit QWindowsOleEnumFmtEtc(const QList<FORMATETC> &fmtetcs); + explicit QWindowsOleEnumFmtEtc(const QList<LPFORMATETC> &lpfmtetcs); ~QWindowsOleEnumFmtEtc() override; bool isNull() const; // IEnumFORMATETC methods - STDMETHOD(Next)(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched); - STDMETHOD(Skip)(ULONG celt); - STDMETHOD(Reset)(void); - STDMETHOD(Clone)(LPENUMFORMATETC FAR* newEnum); + STDMETHOD(Next)(ULONG celt, LPFORMATETC rgelt, ULONG FAR *pceltFetched) override; + STDMETHOD(Skip)(ULONG celt) override; + STDMETHOD(Reset)(void) override; + STDMETHOD(Clone)(LPENUMFORMATETC FAR *newEnum) override; private: bool copyFormatEtc(LPFORMATETC dest, const FORMATETC *src) const; ULONG m_nIndex = 0; - QVector<LPFORMATETC> m_lpfmtetcs; + QList<LPFORMATETC> m_lpfmtetcs; bool m_isNull = false; }; diff --git a/src/plugins/platforms/windows/qwindowsopenglcontext.h b/src/plugins/platforms/windows/qwindowsopenglcontext.h index 1416a7e575..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 @@ -57,6 +21,7 @@ public: virtual ~QWindowsStaticOpenGLContext() = default; virtual QWindowsOpenGLContext *createContext(QOpenGLContext *context) = 0; + virtual QWindowsOpenGLContext *createContext(HGLRC context, HWND window) = 0; virtual void *moduleHandle() const = 0; virtual QOpenGLContext::OpenGLModuleType moduleType() const = 0; virtual bool supportsThreadedOpenGL() const { return false; } @@ -77,9 +42,6 @@ class QWindowsOpenGLContext : public QPlatformOpenGLContext { Q_DISABLE_COPY_MOVE(QWindowsOpenGLContext) public: - // Returns the native context handle (e.g. HGLRC for WGL, EGLContext for EGL). - virtual void *nativeContext() const = 0; - // These should be implemented only for some winsys interfaces, for example EGL. // For others, like WGL, they are not relevant. virtual void *nativeDisplay() const { return nullptr; } diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp index afc1991e2c..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 @@ -69,7 +35,7 @@ static GpuDescription adapterIdentifierToGpuDescription(const D3DADAPTER_IDENTIF result.deviceId = adapterIdentifier.DeviceId; result.revision = adapterIdentifier.Revision; result.subSysId = adapterIdentifier.SubSysId; - QVector<int> version(4, 0); + QList<int> version(4, 0); version[0] = HIWORD(adapterIdentifier.DriverVersion.HighPart); // Product version[1] = LOWORD(adapterIdentifier.DriverVersion.HighPart); // Version version[2] = HIWORD(adapterIdentifier.DriverVersion.LowPart); // Sub version @@ -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() @@ -169,9 +128,9 @@ GpuDescription GpuDescription::detect() return result; } -QVector<GpuDescription> GpuDescription::detectAll() +QList<GpuDescription> GpuDescription::detectAll() { - QVector<GpuDescription> result; + QList<GpuDescription> result; QDirect3D9Handle direct3D9; if (const UINT adapterCount = direct3D9.adapterCount()) { for (UINT adp = 0; adp < adapterCount; ++adp) { @@ -206,7 +165,7 @@ QString GpuDescription::toString() const str << " Card name : " << description << "\n Driver Name : " << driverName << "\n Driver Version : " << driverVersion.toString() - << "\n Vendor ID : 0x" << qSetPadChar(QLatin1Char('0')) + << "\n Vendor ID : 0x" << qSetPadChar(u'0') << Qt::uppercasedigits << Qt::hex << qSetFieldWidth(4) << vendorId << "\n Device ID : 0x" << qSetFieldWidth(4) << deviceId << "\n SubSys ID : 0x" << qSetFieldWidth(8) << subSysId @@ -224,50 +183,30 @@ 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; } -QWindowsOpenGLTester::Renderer QWindowsOpenGLTester::requestedGlesRenderer() -{ - const char platformVar[] = "QT_ANGLE_PLATFORM"; - if (qEnvironmentVariableIsSet(platformVar)) { - const QByteArray anglePlatform = qgetenv(platformVar); - if (anglePlatform == "d3d11") - return QWindowsOpenGLTester::AngleRendererD3d11; - if (anglePlatform == "d3d9") - return QWindowsOpenGLTester::AngleRendererD3d9; - if (anglePlatform == "warp") - return QWindowsOpenGLTester::AngleRendererD3d11Warp; - qCWarning(lcQpaGl) << "Invalid value set for " << platformVar << ": " << anglePlatform; - } - return QWindowsOpenGLTester::InvalidRenderer; -} - QWindowsOpenGLTester::Renderer QWindowsOpenGLTester::requestedRenderer() { const char openGlVar[] = "QT_OPENGL"; - if (QCoreApplication::testAttribute(Qt::AA_UseOpenGLES)) { - const Renderer glesRenderer = QWindowsOpenGLTester::requestedGlesRenderer(); - return glesRenderer != InvalidRenderer ? glesRenderer : Gles; - } + if (QCoreApplication::testAttribute(Qt::AA_UseOpenGLES)) + qWarning("Qt::AA_UseOpenGLES is no longer supported in Qt 6"); if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL)) return QWindowsOpenGLTester::DesktopGl; if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL)) return QWindowsOpenGLTester::SoftwareRasterizer; if (qEnvironmentVariableIsSet(openGlVar)) { const QByteArray requested = qgetenv(openGlVar); - if (requested == "angle") { - const Renderer glesRenderer = QWindowsOpenGLTester::requestedGlesRenderer(); - return glesRenderer != InvalidRenderer ? glesRenderer : Gles; - } + if (requested == "angle") + qWarning("QT_OPENGL=angle is no longer supported in Qt 6"); if (requested == "desktop") return QWindowsOpenGLTester::DesktopGl; if (requested == "software") @@ -283,9 +222,9 @@ static inline QString resolveBugListFile(const QString &fileName) return fileName; // Try QLibraryInfo::SettingsPath which is typically empty unless specified in qt.conf, // then resolve via QStandardPaths::ConfigLocation. - const QString settingsPath = QLibraryInfo::location(QLibraryInfo::SettingsPath); + const QString settingsPath = QLibraryInfo::path(QLibraryInfo::SettingsPath); if (!settingsPath.isEmpty()) { // SettingsPath is empty unless specified in qt.conf. - const QFileInfo fi(settingsPath + QLatin1Char('/') + fileName); + const QFileInfo fi(settingsPath + u'/' + fileName); if (fi.isFile()) return fi.absoluteFilePath(); } @@ -301,9 +240,9 @@ QWindowsOpenGLTester::Renderers QWindowsOpenGLTester::detectSupportedRenderers(c Renderer requested) { #if defined(QT_NO_OPENGL) - Q_UNUSED(gpu) - Q_UNUSED(requested) - return 0; + Q_UNUSED(gpu); + Q_UNUSED(requested); + return {}; #else QOpenGLConfig::Gpu qgpu = QOpenGLConfig::Gpu::fromDevice(gpu.vendorId, gpu.deviceId, gpu.driverVersion, gpu.description); SupportedRenderersCache *srCache = supportedRenderersCache(); @@ -311,46 +250,29 @@ QWindowsOpenGLTester::Renderers QWindowsOpenGLTester::detectSupportedRenderers(c if (it != srCache->cend()) return *it; - QWindowsOpenGLTester::Renderers result(QWindowsOpenGLTester::AngleRendererD3d11 - | QWindowsOpenGLTester::AngleRendererD3d9 - | QWindowsOpenGLTester::AngleRendererD3d11Warp - | QWindowsOpenGLTester::SoftwareRasterizer); + QWindowsOpenGLTester::Renderers result(QWindowsOpenGLTester::SoftwareRasterizer); // Don't test for GL if explicitly requested or GLES only is requested - if (requested == DesktopGl - || ((requested & GlesMask) == 0 && testDesktopGL())) { - result |= QWindowsOpenGLTester::DesktopGl; - } - - const char bugListFileVar[] = "QT_OPENGL_BUGLIST"; - QString buglistFileName = QStringLiteral(":/qt-project.org/windows/openglblacklists/default.json"); - - if (qEnvironmentVariableIsSet(bugListFileVar)) { - const QString fileName = resolveBugListFile(QFile::decodeName(qgetenv(bugListFileVar))); - if (!fileName.isEmpty()) - buglistFileName = fileName; + if (requested == DesktopGl || testDesktopGL()) + result |= QWindowsOpenGLTester::DesktopGl; + + QSet<QString> features; // empty by default -> nothing gets disabled + if (!qEnvironmentVariableIsSet("QT_NO_OPENGL_BUGLIST")) { + const char bugListFileVar[] = "QT_OPENGL_BUGLIST"; + QString buglistFileName = QStringLiteral(":/qt-project.org/windows/openglblacklists/default.json"); + if (qEnvironmentVariableIsSet(bugListFileVar)) { + const QString fileName = resolveBugListFile(QFile::decodeName(qgetenv(bugListFileVar))); + if (!fileName.isEmpty()) + buglistFileName = fileName; + } + features = QOpenGLConfig::gpuFeatures(qgpu, buglistFileName); } - - QSet<QString> features = QOpenGLConfig::gpuFeatures(qgpu, buglistFileName); qCDebug(lcQpaGl) << "GPU features:" << features; if (features.contains(QStringLiteral("disable_desktopgl"))) { // Qt-specific qCDebug(lcQpaGl) << "Disabling Desktop GL: " << gpu; result &= ~QWindowsOpenGLTester::DesktopGl; } - if (features.contains(QStringLiteral("disable_angle"))) { // Qt-specific keyword - qCDebug(lcQpaGl) << "Disabling ANGLE: " << gpu; - result &= ~QWindowsOpenGLTester::GlesMask; - } else { - if (features.contains(QStringLiteral("disable_d3d11"))) { // standard keyword - qCDebug(lcQpaGl) << "Disabling D3D11: " << gpu; - result &= ~QWindowsOpenGLTester::AngleRendererD3d11; - } - if (features.contains(QStringLiteral("disable_d3d9"))) { // Qt-specific - qCDebug(lcQpaGl) << "Disabling D3D9: " << gpu; - result &= ~QWindowsOpenGLTester::AngleRendererD3d9; - } - } if (features.contains(QStringLiteral("disable_rotation"))) { qCDebug(lcQpaGl) << "Disabling rotation: " << gpu; result |= DisableRotationFlag; @@ -395,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 9576dfbae0..abda0c2dc1 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.h +++ b/src/plugins/platforms/windows/qwindowsopengltester.h @@ -1,48 +1,12 @@ -/**************************************************************************** -** -** 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 #include <QtCore/qbytearray.h> #include <QtCore/qflags.h> -#include <QtCore/qvector.h> +#include <QtCore/qlist.h> #include <QtCore/qversionnumber.h> QT_BEGIN_NAMESPACE @@ -53,7 +17,7 @@ class QVariant; struct GpuDescription { static GpuDescription detect(); - static QVector<GpuDescription> detectAll(); + static QList<GpuDescription> detectAll(); QString toString() const; QVariant toVariant() const; @@ -77,12 +41,6 @@ public: enum Renderer { InvalidRenderer = 0x0000, DesktopGl = 0x0001, - AngleRendererD3d11 = 0x0002, - AngleRendererD3d9 = 0x0004, - AngleRendererD3d11Warp = 0x0008, // "Windows Advanced Rasterization Platform" - AngleBackendMask = AngleRendererD3d11 | AngleRendererD3d9 | AngleRendererD3d11Warp, - Gles = 0x0010, // ANGLE/unspecified or Generic GLES for Windows CE. - GlesMask = Gles | AngleBackendMask, SoftwareRasterizer = 0x0020, RendererMask = 0x00FF, DisableRotationFlag = 0x0100, @@ -90,7 +48,6 @@ public: }; Q_DECLARE_FLAGS(Renderers, Renderer) - static Renderer requestedGlesRenderer(); static Renderer requestedRenderer(); static QWindowsOpenGLTester::Renderers supportedRenderers(Renderer requested); diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index fba24d8696..71c7217671 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -1,50 +1,13 @@ -/**************************************************************************** -** -** 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" +#if QT_CONFIG(tabletevent) +# include "qwindowstabletsupport.h" +#endif #include "qwindowskeymapper.h" #include "qwindowscontext.h" #include "qwindowswindow.h" @@ -53,12 +16,11 @@ #include <QtGui/qguiapplication.h> #include <QtGui/qscreen.h> -#include <QtGui/qtouchdevice.h> +#include <QtGui/qpointingdevice.h> #include <QtGui/qwindow.h> #include <QtGui/private/qguiapplication_p.h> #include <QtCore/qvarlengtharray.h> #include <QtCore/qloggingcategory.h> -#include <QtCore/qoperatingsystemversion.h> #include <QtCore/qqueue.h> #include <algorithm> @@ -75,12 +37,18 @@ enum { QT_PT_TOUCHPAD = 5, // MinGW is missing PT_TOUCHPAD }; +qint64 QWindowsPointerHandler::m_nextInputDeviceId = 1; + +QWindowsPointerHandler::~QWindowsPointerHandler() +{ +} + bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result) { *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; } @@ -94,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; } @@ -112,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; } @@ -133,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; } @@ -145,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; } @@ -190,11 +156,11 @@ static inline MouseEvent eventFromMsg(const MSG &msg) case WM_LBUTTONDBLCLK: // Qt QPA does not handle double clicks, send as press return {QEvent::MouseButtonPress, Qt::LeftButton}; case WM_MBUTTONDOWN: - return {QEvent::MouseButtonPress, Qt::MidButton}; + return {QEvent::MouseButtonPress, Qt::MiddleButton}; case WM_MBUTTONUP: - return {QEvent::MouseButtonRelease, Qt::MidButton}; + return {QEvent::MouseButtonRelease, Qt::MiddleButton}; case WM_MBUTTONDBLCLK: - return {QEvent::MouseButtonPress, Qt::MidButton}; + return {QEvent::MouseButtonPress, Qt::MiddleButton}; case WM_RBUTTONDOWN: return {QEvent::MouseButtonPress, Qt::RightButton}; case WM_RBUTTONUP: @@ -216,11 +182,11 @@ static inline MouseEvent eventFromMsg(const MSG &msg) case WM_NCLBUTTONDBLCLK: return {QEvent::NonClientAreaMouseButtonPress, Qt::LeftButton}; case WM_NCMBUTTONDOWN: - return {QEvent::NonClientAreaMouseButtonPress, Qt::MidButton}; + return {QEvent::NonClientAreaMouseButtonPress, Qt::MiddleButton}; case WM_NCMBUTTONUP: - return {QEvent::NonClientAreaMouseButtonRelease, Qt::MidButton}; + return {QEvent::NonClientAreaMouseButtonRelease, Qt::MiddleButton}; case WM_NCMBUTTONDBLCLK: - return {QEvent::NonClientAreaMouseButtonPress, Qt::MidButton}; + return {QEvent::NonClientAreaMouseButtonPress, Qt::MiddleButton}; case WM_NCRBUTTONDOWN: return {QEvent::NonClientAreaMouseButtonPress, Qt::RightButton}; case WM_NCRBUTTONUP: @@ -258,7 +224,7 @@ static Qt::MouseButtons queryMouseButtons() if (GetAsyncKeyState(VK_RBUTTON) < 0) result |= mouseSwapped ? Qt::LeftButton : Qt::RightButton; if (GetAsyncKeyState(VK_MBUTTON) < 0) - result |= Qt::MidButton; + result |= Qt::MiddleButton; if (GetAsyncKeyState(VK_XBUTTON1) < 0) result |= Qt::XButton1; if (GetAsyncKeyState(VK_XBUTTON2) < 0) @@ -310,32 +276,38 @@ static bool isValidWheelReceiver(QWindow *candidate) return false; } -static QTouchDevice *createTouchDevice() -{ - const int digitizers = GetSystemMetrics(SM_DIGITIZER); - if (!(digitizers & (NID_INTEGRATED_TOUCH | NID_EXTERNAL_TOUCH))) - return nullptr; - const int tabletPc = GetSystemMetrics(SM_TABLETPC); - const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES); - qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << (digitizers & ~NID_READY) - << "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase - << "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints; - auto *result = new QTouchDevice; - result->setType(digitizers & NID_INTEGRATED_TOUCH - ? QTouchDevice::TouchScreen : QTouchDevice::TouchPad); - QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition; - if (result->type() == QTouchDevice::TouchPad) - capabilities |= QTouchDevice::MouseEmulation; - result->setCapabilities(capabilities); - result->setMaximumTouchPoints(maxTouchPoints); - return result; -} - -QTouchDevice *QWindowsPointerHandler::ensureTouchDevice() +QWindowsPointerHandler::QPointingDevicePtr QWindowsPointerHandler::createTouchDevice(bool mouseEmulation) { - if (!m_touchDevice) - m_touchDevice = createTouchDevice(); - return m_touchDevice; + const int digitizers = GetSystemMetrics(SM_DIGITIZER); + if (!(digitizers & (NID_INTEGRATED_TOUCH | NID_EXTERNAL_TOUCH))) + return nullptr; + const int tabletPc = GetSystemMetrics(SM_TABLETPC); + const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES); + const QPointingDevice::DeviceType type = (digitizers & NID_INTEGRATED_TOUCH) + ? QInputDevice::DeviceType::TouchScreen : QInputDevice::DeviceType::TouchPad; + QInputDevice::Capabilities capabilities = QInputDevice::Capability::Position + | QInputDevice::Capability::Area + | QInputDevice::Capability::NormalizedPosition; + if (type != QInputDevice::DeviceType::TouchScreen) { + capabilities.setFlag(QInputDevice::Capability::MouseEmulation); + capabilities.setFlag(QInputDevice::Capability::Scroll); + } else if (mouseEmulation) { + capabilities.setFlag(QInputDevice::Capability::MouseEmulation); + } + + const int flags = digitizers & ~NID_READY; + qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << flags + << "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase + << "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints << "Capabilities:" << capabilities; + + const int buttonCount = type == QInputDevice::DeviceType::TouchScreen ? 1 : 3; + // TODO: use system-provided name and device ID rather than empty-string and m_nextInputDeviceId + const qint64 systemId = m_nextInputDeviceId++ | (qint64(flags << 2)); + auto d = new QPointingDevice(QString(), systemId, type, + QPointingDevice::PointerType::Finger, + capabilities, maxTouchPoints, buttonCount, + QString(), QPointingDeviceUniqueId::fromNumericId(systemId)); + return QPointingDevicePtr(d); } void QWindowsPointerHandler::clearEvents() @@ -447,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. @@ -454,12 +428,24 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, return false; if (msg.message == WM_POINTERCAPTURECHANGED) { - QWindowSystemInterface::handleTouchCancelEvent(window, m_touchDevice, - 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; @@ -470,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; @@ -482,7 +466,8 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, << " message=" << Qt::hex << msg.message << " count=" << Qt::dec << count; - Qt::TouchPointStates allStates; + QEventPoint::States allStates; + QSet<int> inputIds; for (quint32 i = 0; i < count; ++i) { if (QWindowsContext::verbose > 1) @@ -495,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); @@ -517,32 +505,58 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, touchPoint.normalPosition = normalPosition; if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) { - touchPoint.state = Qt::TouchPointPressed; - m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition); + touchPoint.state = QEventPoint::State::Pressed; + m_lastTouchPoints.insert(touchPoint.id, touchPoint); } else if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_UP) { - touchPoint.state = Qt::TouchPointReleased; - m_lastTouchPositions.remove(touchPoint.id); + touchPoint.state = QEventPoint::State::Released; + m_lastTouchPoints.remove(touchPoint.id); } else { - touchPoint.state = stationaryTouchPoint ? Qt::TouchPointStationary : Qt::TouchPointMoved; - m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition); + touchPoint.state = stationaryTouchPoint ? QEventPoint::State::Stationary : QEventPoint::State::Updated; + 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 == Qt::TouchPointReleased) + if (allStates == QEventPoint::State::Released) m_touchInputIDToTouchPointID.clear(); - QWindowSystemInterface::handleTouchEvent(window, m_touchDevice, 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. } +#if QT_CONFIG(tabletevent) +QWindowsPointerHandler::QPointingDevicePtr QWindowsPointerHandler::findTabletDevice(QPointingDevice::PointerType pointerType) const +{ + for (const auto &d : m_tabletDevices) { + if (d->pointerType() == pointerType) + return d; + } + return {}; +} +#endif + bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vPenInfo) { @@ -553,32 +567,35 @@ 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 sourceDevice = (qint64)penInfo->pointerInfo.sourceDevice; + const auto systemId = (qint64)penInfo->pointerInfo.sourceDevice; const QPoint globalPos = QPoint(penInfo->pointerInfo.ptPixelLocation.x, penInfo->pointerInfo.ptPixelLocation.y); const QPoint localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPos); const QPointF hiResGlobalPos = QPointF(dRect.left + qreal(penInfo->pointerInfo.ptHimetricLocation.x - pRect.left) / (pRect.right - pRect.left) * (dRect.right - dRect.left), dRect.top + qreal(penInfo->pointerInfo.ptHimetricLocation.y - pRect.top) / (pRect.bottom - pRect.top) * (dRect.bottom - dRect.top)); - const qreal pressure = (penInfo->penMask & PEN_MASK_PRESSURE) ? qreal(penInfo->pressure) / 1024.0 : 0.5; - const qreal rotation = (penInfo->penMask & PEN_MASK_ROTATION) ? qreal(penInfo->rotation) : 0.0; + const bool hasPressure = (penInfo->penMask & PEN_MASK_PRESSURE) != 0; + const bool hasRotation = (penInfo->penMask & PEN_MASK_ROTATION) != 0; + const qreal pressure = hasPressure ? qreal(penInfo->pressure) / 1024.0 : 0.5; + const qreal rotation = hasRotation ? qreal(penInfo->rotation) : 0.0; const qreal tangentialPressure = 0.0; - const int xTilt = (penInfo->penMask & PEN_MASK_TILT_X) ? penInfo->tiltX : 0; - const int yTilt = (penInfo->penMask & PEN_MASK_TILT_Y) ? penInfo->tiltY : 0; + const bool hasTiltX = (penInfo->penMask & PEN_MASK_TILT_X) != 0; + const bool hasTiltY = (penInfo->penMask & PEN_MASK_TILT_Y) != 0; + const int xTilt = hasTiltX ? penInfo->tiltX : 0; + const int yTilt = hasTiltY ? penInfo->tiltY : 0; const int z = 0; if (QWindowsContext::verbose > 1) qCDebug(lcQpaEvents).noquote().nospace() << Qt::showbase - << __FUNCTION__ << " sourceDevice=" << sourceDevice + << __FUNCTION__ << " systemId=" << systemId << " globalPos=" << globalPos << " localPos=" << localPos << " hiResGlobalPos=" << hiResGlobalPos << " message=" << Qt::hex << msg.message << " flags=" << Qt::hex << penInfo->pointerInfo.pointerFlags; - const QTabletEvent::TabletDevice device = QTabletEvent::Stylus; - QTabletEvent::PointerType type; + QPointingDevice::PointerType type; // Since it may be the middle button, so if the checks fail then it should // be set to Middle if it was used. Qt::MouseButtons mouseButtons = queryMouseButtons(); @@ -588,16 +605,41 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin mouseButtons = Qt::LeftButton; if (penInfo->penFlags & (PEN_FLAG_ERASER | PEN_FLAG_INVERTED)) { - type = QTabletEvent::Eraser; + type = QPointingDevice::PointerType::Eraser; } else { - type = QTabletEvent::Pen; + type = QPointingDevice::PointerType::Pen; if (pointerInContact && penInfo->penFlags & PEN_FLAG_BARREL) mouseButtons = Qt::RightButton; // Either left or right, not both } + auto device = findTabletDevice(type); + if (device.isNull()) { + QInputDevice::Capabilities caps(QInputDevice::Capability::Position + | QInputDevice::Capability::MouseEmulation + | QInputDevice::Capability::Hover); + if (hasPressure) + caps |= QInputDevice::Capability::Pressure; + if (hasRotation) + caps |= QInputDevice::Capability::Rotation; + if (hasTiltX) + caps |= QInputDevice::Capability::XTilt; + if (hasTiltY) + caps |= QInputDevice::Capability::YTilt; + const qint64 uniqueId = systemId | (qint64(type) << 32L); + device.reset(new QPointingDevice(QStringLiteral("wmpointer"), + systemId, QInputDevice::DeviceType::Stylus, + type, caps, 1, 3, QString(), + QPointingDeviceUniqueId::fromNumericId(uniqueId))); + QWindowSystemInterface::registerInputDevice(device.data()); + m_tabletDevices.append(device); + } + + const auto uniqueId = device->uniqueId().numericId(); + m_activeTabletDevice = device; + switch (msg.message) { case WM_POINTERENTER: { - QWindowSystemInterface::handleTabletEnterProximityEvent(device, type, sourceDevice); + 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. @@ -610,12 +652,12 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin m_windowUnderPointer = nullptr; m_currentWindow = nullptr; } - QWindowSystemInterface::handleTabletLeaveProximityEvent(device, type, sourceDevice); + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, msg.time, device.data(), false); break; case WM_POINTERDOWN: case WM_POINTERUP: case WM_POINTERUPDATE: { - QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(sourceDevice).target; // Pass to window that grabbed it. + QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(uniqueId).target; // Pass to window that grabbed it. if (!target && m_windowUnderPointer) target = m_windowUnderPointer; if (!target) @@ -633,11 +675,13 @@ 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, localPos, hiResGlobalPos, device, type, mouseButtons, - pressure, xTilt, yTilt, tangentialPressure, rotation, z, - sourceDevice, keyModifiers); + QWindowSystemInterface::handleTabletEvent(target, msg.time, device.data(), + localPos, hiResGlobalPos, mouseButtons, + pressure, xTilt, yTilt, tangentialPressure, + rotation, z, keyModifiers); return false; // Allow mouse messages to be generated. } } @@ -685,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; } @@ -698,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) @@ -728,21 +778,35 @@ 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; } Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; + const QPointingDevice *device = QWindowsMouseHandler::primaryMouse(); + // Following the logic of the old mouse handler, only events synthesized // for touch screen are marked as such. On some systems, using the bit 7 of // the extra msg info for checking if synthesized for touch does not work, // so we use the pointer type of the last pointer message. - if (isMouseEventSynthesizedFromPenOrTouch() && m_pointerType == QT_PT_TOUCH) { - if (QWindowsIntegration::instance()->options() & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch) + if (isMouseEventSynthesizedFromPenOrTouch()) { + switch (m_pointerType) { + case QT_PT_TOUCH: + if (QWindowsIntegration::instance()->options() & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch) + return false; + source = Qt::MouseEventSynthesizedBySystem; + if (!m_touchDevice.isNull()) + device = m_touchDevice.data(); + break; + case QT_PT_PEN: +#if QT_CONFIG(tabletevent) + qCDebug(lcQpaTablet) << "ignoring synth-mouse event for tablet event from" << device; return false; - source = Qt::MouseEventSynthesizedBySystem; +#endif + break; + } } const MouseEvent mouseEvent = eventFromMsg(msg); @@ -762,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, localPos, globalPos, mouseButtons, m_lastEventButton, - QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source); - } else { - QWindowSystemInterface::handleMouseEvent(window, 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, localPos, globalPos, mouseButtons, + QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons, mouseEvent.button, mouseEvent.type, keyModifiers, source); return false; // Allow further event processing } @@ -794,7 +855,7 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, handleEnterLeave(window, currentWindowUnderPointer, globalPos); if (!discardEvent && mouseEvent.type != QEvent::None) { - QWindowSystemInterface::handleMouseEvent(window, 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 8874db27e3..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 @@ -45,23 +9,31 @@ #include <QtCore/qpointer.h> #include <QtCore/qscopedpointer.h> +#include <QtCore/qsharedpointer.h> #include <QtCore/qhash.h> #include <QtGui/qevent.h> +#include <qpa/qwindowsysteminterface.h> QT_BEGIN_NAMESPACE class QWindow; -class QTouchDevice; +class QPointingDevice; class QWindowsPointerHandler { Q_DISABLE_COPY_MOVE(QWindowsPointerHandler) public: + using QPointingDevicePtr = QSharedPointer<QPointingDevice>; + QWindowsPointerHandler() = default; + ~QWindowsPointerHandler(); bool translatePointerEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result); bool translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result); - QTouchDevice *touchDevice() const { return m_touchDevice; } - QTouchDevice *ensureTouchDevice(); + + const QPointingDevicePtr &touchDevice() const { return m_touchDevice; } + void setTouchDevice(const QPointingDevicePtr &d) { m_touchDevice = d; } + static QPointingDevicePtr createTouchDevice(bool mouseEmulation); + QWindow *windowUnderMouse() const { return m_windowUnderPointer.data(); } void clearWindowUnderMouse() { m_windowUnderPointer = nullptr; } void clearEvents(); @@ -72,9 +44,16 @@ private: bool translateMouseWheelEvent(QWindow *window, QWindow *currentWindowUnderPointer, MSG msg, QPoint globalPos, Qt::KeyboardModifiers keyModifiers); void handleCaptureRelease(QWindow *window, QWindow *currentWindowUnderPointer, HWND hwnd, QEvent::Type eventType, Qt::MouseButtons mouseButtons); void handleEnterLeave(QWindow *window, QWindow *currentWindowUnderPointer, QPoint globalPos); +#if QT_CONFIG(tabletevent) + QPointingDevicePtr findTabletDevice(QPointingDevice::PointerType pointerType) const; +#endif - QTouchDevice *m_touchDevice = nullptr; - QHash<int, QPointF> m_lastTouchPositions; + QPointingDevicePtr m_touchDevice; +#if QT_CONFIG(tabletevent) + QList<QPointingDevicePtr> m_tabletDevices; + QPointingDevicePtr m_activeTabletDevice; +#endif + QHash<int, QWindowSystemInterface::TouchPoint> m_lastTouchPoints; QHash<DWORD, int> m_touchInputIDToTouchPointID; QPointer<QWindow> m_windowUnderPointer; QPointer<QWindow> m_currentWindow; @@ -83,6 +62,7 @@ private: QEvent::Type m_lastEventType = QEvent::None; Qt::MouseButton m_lastEventButton = Qt::NoButton; DWORD m_pointerType = 0; + static qint64 m_nextInputDeviceId; // workaround until we know how to get system device IDs }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 4f76a82544..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,13 +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)); @@ -64,16 +43,222 @@ 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}; } -using WindowsScreenDataList = QVector<QWindowsScreenData>; +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; + std::vector<DISPLAYCONFIG_MODE_INFO> modeInfos; + + // Fetch paths + LONG result; + UINT32 numPathArrayElements; + UINT32 numModeInfoArrayElements; + do { + // QueryDisplayConfig documentation doesn't say the number of needed elements is updated + // when the call fails with ERROR_INSUFFICIENT_BUFFER, so we need a separate call to + // look up the needed buffer sizes. + if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements, + &numModeInfoArrayElements) != ERROR_SUCCESS) { + return {}; + } + pathInfos.resize(numPathArrayElements); + modeInfos.resize(numModeInfoArrayElements); + result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements, pathInfos.data(), + &numModeInfoArrayElements, modeInfos.data(), nullptr); + } while (result == ERROR_INSUFFICIENT_BUFFER); + + if (result != ERROR_SUCCESS) + 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 pathInfos; +} + +#if 0 +// Needed later for HDR support +static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo) +{ + const float defaultSdrWhiteLevel = 200.0; + if (!targetInfo) + return defaultSdrWhiteLevel; + + DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {}; + whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL; + whiteLevel.header.size = sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL); + whiteLevel.header.adapterId = targetInfo->adapterId; + whiteLevel.header.id = targetInfo->id; + if (DisplayConfigGetDeviceInfo(&whiteLevel.header) != ERROR_SUCCESS) + return defaultSdrWhiteLevel; + return whiteLevel.SDRWhiteLevel * 80.0 / 1000.0; +} +#endif + +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) { @@ -86,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 == QLatin1String("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)) { @@ -102,12 +293,41 @@ 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 - data->orientation = data->geometry.height() > data->geometry.width() ? - Qt::PortraitOrientation : Qt::LandscapeOrientation; + + // ### We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO, + // if we are going to use DISPLAYCONFIG lookups more. + 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; + break; + case DISPLAYCONFIG_ROTATION_ROTATE90: + data->orientation = Qt::PortraitOrientation; + break; + case DISPLAYCONFIG_ROTATION_ROTATE180: + data->orientation = Qt::InvertedLandscapeOrientation; + break; + case DISPLAYCONFIG_ROTATION_ROTATE270: + data->orientation = Qt::InvertedPortraitOrientation; + break; + case DISPLAYCONFIG_ROTATION_FORCE_UINT32: + Q_UNREACHABLE(); + break; + } + if (pathInfo.targetInfo.refreshRate.Numerator && pathInfo.targetInfo.refreshRate.Denominator) + data->refreshRateHz = static_cast<qreal>(pathInfo.targetInfo.refreshRate.Numerator) + / pathInfo.targetInfo.refreshRate.Denominator; + } else { + data->orientation = data->geometry.height() > data->geometry.width() + ? Qt::PortraitOrientation + : Qt::LandscapeOrientation; + } // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only // virtual desktop screens. data->flags |= QWindowsScreenData::VirtualDesktop; @@ -116,12 +336,22 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) return true; } -// from QDesktopWidget, taking WindowsScreenDataList as LPARAM +// from monitorData, taking WindowsScreenDataList as LPARAM BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p) { 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 @@ -148,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) @@ -171,7 +401,6 @@ static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d) \brief Windows screen. \sa QWindowsScreenManager \internal - \ingroup qt-lighthouse-win */ QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) : @@ -182,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 { @@ -242,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; } @@ -253,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; } @@ -289,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; @@ -302,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(), @@ -318,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 @@ -334,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; @@ -402,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; @@ -432,55 +660,68 @@ QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTy \sa QWindowsScreen \internal - \ingroup qt-lighthouse-win */ -QWindowsScreenManager::QWindowsScreenManager() = default; +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); +} -bool QWindowsScreenManager::isSingleScreen() +QWindowsScreenManager::QWindowsScreenManager() = default; + +void QWindowsScreenManager::initialize() { - return QWindowsContext::instance()->screenManager().screens().size() < 2; -} + qCDebug(lcQpaScreen) << "Initializing screen manager"; -/*! - \brief Triggers synchronization of screens (WM_DISPLAYCHANGE). + auto className = QWindowsContext::instance()->registerWindowClass( + QWindowsContext::classNamePrefix() + QLatin1String("ScreenChangeObserverWindow"), + qDisplayChangeObserverWndProc); - Subsequent events are compressed since WM_DISPLAYCHANGE is sent to - each top level window. -*/ + // 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); -bool QWindowsScreenManager::handleDisplayChange(WPARAM wParam, LPARAM lParam) -{ - const int newDepth = int(wParam); - const WORD newHorizontalResolution = LOWORD(lParam); - const WORD newVerticalResolution = HIWORD(lParam); - if (newDepth != m_lastDepth || newHorizontalResolution != m_lastHorizontalResolution - || newVerticalResolution != m_lastVerticalResolution) { - m_lastDepth = newDepth; - m_lastHorizontalResolution = newHorizontalResolution; - m_lastVerticalResolution = newVerticalResolution; - qCDebug(lcQpaWindows) << __FUNCTION__ << "Depth=" << newDepth - << ", resolution " << newHorizontalResolution << 'x' << newVerticalResolution; - handleScreenChanges(); - } - return false; + qCDebug(lcQpaScreen) << "Created display change observer" << m_displayChangeObserver; + + handleScreenChanges(); +} + +QWindowsScreenManager::~QWindowsScreenManager() +{ + DestroyWindow(m_displayChangeObserver); +} + +bool QWindowsScreenManager::isSingleScreen() +{ + return QWindowsContext::instance()->screenManager().screens().size() < 2; } 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; } @@ -504,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 @@ -546,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) @@ -556,19 +797,21 @@ 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 - if (primaryScreenChanged) - QWindowsTheme::instance()->refreshFonts(); + if (primaryScreenChanged) { + if (auto theme = QWindowsTheme::instance()) // QTBUG-85734/Wine + theme->refreshFonts(); + } return true; } diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 5c095808f2..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,10 +7,9 @@ #include "qtwindowsglobal.h" #include <QtCore/qlist.h> -#include <QtCore/qvector.h> -#include <QtCore/qpair.h> #include <QtCore/qscopedpointer.h> #include <qpa/qplatformscreen.h> +#include <QtGui/qscreen_platform.h> QT_BEGIN_NAMESPACE @@ -67,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 @@ -87,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; @@ -104,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(); } @@ -116,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; @@ -126,15 +99,17 @@ private: class QWindowsScreenManager { + Q_DISABLE_COPY_MOVE(QWindowsScreenManager) public: - using WindowsScreenList = QVector<QWindowsScreen *>; + using WindowsScreenList = QList<QWindowsScreen *>; QWindowsScreenManager(); + void initialize(); + ~QWindowsScreenManager(); void clearScreens(); bool handleScreenChanges(); - bool handleDisplayChange(WPARAM wParam, LPARAM lParam); const WindowsScreenList &screens() const { return m_screens; } const QWindowsScreen *screenAtDp(const QPoint &p) const; @@ -145,10 +120,8 @@ public: private: void removeScreen(int index); + HWND m_displayChangeObserver = nullptr; WindowsScreenList m_screens; - int m_lastDepth = -1; - WORD m_lastHorizontalResolution = 0; - WORD m_lastVerticalResolution = 0; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index 83b052bb49..89f93fd161 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -1,71 +1,118 @@ -/**************************************************************************** -** -** 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> #include <QtCore/qurl.h> #include <QtCore/qdebug.h> #include <QtCore/qdir.h> +#include <QtCore/qscopedpointer.h> +#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 *operation, const wchar_t *file, + const wchar_t *parameters) + : m_operation(operation) + , m_file(file) + , m_parameters(parameters) { } + + void run() override + { + 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_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() ? QDir::toNativeSeparators(url.toLocalFile()) : url.toString(QUrl::FullyEncoded); - const auto result = - reinterpret_cast<quintptr>(ShellExecute(nullptr, nullptr, - reinterpret_cast<const wchar_t *>(nativeFilePath.utf16()), - nullptr, nullptr, SW_SHOWNORMAL)); + + + // 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(nullptr, + reinterpret_cast<const wchar_t *>(nativeFilePath.utf16()), + nullptr); + thread.start(); + thread.wait(); + + const auto result = reinterpret_cast<quintptr>(thread.result()); + // ShellExecute returns a value greater than 32 if successful if (result <= 32) { - qWarning("ShellExecute '%s' failed (error %s).", qPrintable(url.toString()), qPrintable(QString::number(result))); + qWarning("%s", qPrintable(msgShellExecuteFailed(url, result))); return false; } return true; @@ -83,16 +130,16 @@ 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. - if (command.isEmpty() || command.contains(QLatin1String(",MailToProtocolHandler"))) + // 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}; return ExpandEnvironmentStrings(reinterpret_cast<const wchar_t *>(command.utf16()), @@ -104,13 +151,18 @@ static inline bool launchMail(const QUrl &url) { QString command = mailCommand(); if (command.isEmpty()) { - qWarning("Cannot launch '%s': There is no mail program installed.", qPrintable(url.toString())); + 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 = QLatin1Char('"'); + 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); @@ -118,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 @@ -129,7 +181,7 @@ static inline bool launchMail(const QUrl &url) si.cb = sizeof(si); if (!CreateProcess(nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(command.utf16())), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { - qErrnoWarning("Unable to launch '%s'", qPrintable(command)); + qErrnoWarning("Unable to launch '%ls'", qUtf16Printable(command)); return false; } CloseHandle(pi.hProcess); @@ -140,9 +192,10 @@ static inline bool launchMail(const QUrl &url) bool QWindowsServices::openUrl(const QUrl &url) { const QString scheme = url.scheme(); - if (scheme == QLatin1String("mailto") && launchMail(url)) + 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 f2dba4d06b..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" @@ -57,13 +9,14 @@ #include "qwindowsmenu.h" #include "qwindowsscreen.h" +#include <QtGui/qguiapplication.h> #include <QtGui/qpixmap.h> #include <QtCore/qdebug.h> +#include <QtCore/qlist.h> #include <QtCore/qrect.h> -#include <QtCore/qvector.h> #include <QtCore/qsettings.h> +#include <qpa/qwindowsysteminterface.h> -#include <qt_windows.h> #include <commctrl.h> #include <shellapi.h> #include <shlobj.h> @@ -71,6 +24,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::Literals::StringLiterals; + static const UINT q_uNOTIFYICONID = 0; static uint MYWM_TASKBARCREATED = 0; @@ -117,7 +72,7 @@ struct QWindowsHwndSystemTrayIconEntry QWindowsSystemTrayIcon *trayIcon; }; -using HwndTrayIconEntries = QVector<QWindowsHwndSystemTrayIconEntry>; +using HwndTrayIconEntries = QList<QWindowsHwndSystemTrayIconEntry>; Q_GLOBAL_STATIC(HwndTrayIconEntries, hwndTrayIconEntries) @@ -136,7 +91,7 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsTrayIconWndProc(HWND hwnd, UINT messa { if (message == MYWM_TASKBARCREATED || message == MYWM_NOTIFYICON || message == WM_INITMENU || message == WM_INITMENUPOPUP - || message == WM_COMMAND) { + || message == WM_CLOSE || message == WM_COMMAND) { const int index = indexOfHwnd(hwnd); if (index >= 0) { MSG msg; @@ -163,7 +118,7 @@ static inline HWND createTrayIconMessageWindow() return nullptr; // Register window class in the platform plugin. const QString className = - ctx->registerWindowClass(QStringLiteral("QTrayIconMessageWindowClass"), + ctx->registerWindowClass(QWindowsContext::classNamePrefix() + "TrayIconMessageWindowClass"_L1, qWindowsTrayIconWndProc); const wchar_t windowName[] = L"QTrayIconMessageWindow"; return CreateWindowEx(0, reinterpret_cast<const wchar_t *>(className.utf16()), @@ -179,7 +134,6 @@ static inline HWND createTrayIconMessageWindow() \brief Windows native system tray icon \internal - \ingroup qt-lighthouse-win */ QWindowsSystemTrayIcon::QWindowsSystemTrayIcon() @@ -210,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); @@ -231,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); @@ -256,7 +212,7 @@ void QWindowsSystemTrayIcon::showMessage(const QString &title, const QString &me // For empty messages, ensures that they show when only title is set QString message = messageIn; if (message.isEmpty() && !title.isEmpty()) - message.append(QLatin1Char(' ')); + message.append(u' '); NOTIFYICONDATA tnd; initNotifyIconData(tnd); @@ -264,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 @@ -340,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(); } @@ -357,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; @@ -419,8 +395,12 @@ bool QWindowsSystemTrayIcon::winEvent(const MSG &message, long *result) if (screen) { emit contextMenuRequested(globalPos, screen); emit activated(Context); - if (m_menu) + if (m_menu) { + // Set the foreground window to the controlling window so that clicking outside + // of the menu or window will cause the menu to close + SetForegroundWindow(m_hwnd); m_menu->trackPopupMenu(message.hwnd, globalPos.x(), globalPos.y()); + } } } break; @@ -439,12 +419,22 @@ bool QWindowsSystemTrayIcon::winEvent(const MSG &message, long *result) case WM_INITMENUPOPUP: QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast<HMENU>(message.wParam)); break; + case WM_CLOSE: + QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>(); + break; case WM_COMMAND: 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 cd5a78abb6..ceebb483d2 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.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) 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" @@ -133,11 +97,10 @@ QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t) { QDebugStateSaver saver(d); d.nospace(); - d << "TabletDevice id:" << t.uniqueId << " pressure: " << t.minPressure + d << "TabletDevice id:" << t.systemId << " pressure: " << t.minPressure << ".." << t.maxPressure << " tan pressure: " << t.minTanPressure << ".." << t.maxTanPressure << " area: (" << t.minX << ',' << t.minY << ',' << t.minZ - << ")..(" << t.maxX << ',' << t.maxY << ',' << t.maxZ << ") device " - << t.currentDevice << " pointer " << t.currentPointerType; + << ")..(" << t.maxX << ',' << t.maxY << ',' << t.maxZ << ')'; return d; } @@ -166,6 +129,48 @@ QDebug operator<<(QDebug d, const LOGCONTEXT &lc) } #endif // !QT_NO_DEBUG_STREAM +QWinTabPointingDevice *createInputDevice(const QSharedPointer<QWindowsTabletDeviceData> &d, + QInputDevice::DeviceType devType, + QPointingDevice::PointerType pointerType) +{ + const qint64 uniqueId = d->systemId | (qint64(devType) << 32) + | (qint64(pointerType) << 48L); + QInputDevice::Capabilities caps(QInputDevice::Capability::Position + | QInputDevice::Capability::Pressure + | QInputDevice::Capability::MouseEmulation + | QInputDevice::Capability::Hover); + if (d->zCapability) + caps |= QInputDevice::Capability::ZPosition; + if (d->tiltCapability) { + caps |= QInputDevice::Capability::XTilt + | QInputDevice::Capability::YTilt + | QInputDevice::Capability::Rotation + | QInputDevice::Capability::TangentialPressure; + } + + auto result = new QWinTabPointingDevice(d, QStringLiteral("wintab"), d->systemId, + devType, pointerType, caps, 1, + d->buttonsMap.size(), QString(), + QPointingDeviceUniqueId::fromNumericId(uniqueId)); + QWindowSystemInterface::registerInputDevice(result); + return result; +} + +QWinTabPointingDevice::QWinTabPointingDevice(const QWinTabPointingDevice::DeviceDataPtr &data, + const QString &name, qint64 systemId, + QInputDevice::DeviceType devType, + QPointingDevice::PointerType pType, + QInputDevice::Capabilities caps, + int maxPoints, int buttonCount, + const QString &seatName, + QPointingDeviceUniqueId uniqueId, + QObject *parent) + : QPointingDevice(name, systemId, devType, pType, caps, maxPoints, buttonCount, + seatName, uniqueId, parent), + m_deviceData(data) +{ +} + QWindowsWinTab32DLL QWindowsTabletSupport::m_winTab32DLL; /*! @@ -173,7 +178,6 @@ QWindowsWinTab32DLL QWindowsTabletSupport::m_winTab32DLL; \brief Functions from wintabl32.dll shipped with WACOM tablets used by QWindowsTabletSupport. \internal - \ingroup qt-lighthouse-win */ bool QWindowsWinTab32DLL::init() @@ -205,9 +209,10 @@ bool QWindowsWinTab32DLL::init() \internal \since 5.2 - \ingroup qt-lighthouse-win */ +int QWindowsTabletSupport::m_absoluteRange = 20; + QWindowsTabletSupport::QWindowsTabletSupport(HWND window, HCTX context) : m_window(window) , m_context(context) @@ -320,56 +325,103 @@ void QWindowsTabletSupport::notifyActivate() qCDebug(lcQpaTablet) << __FUNCTION__ << result; } -static inline int indexOfDevice(const QVector<QWindowsTabletDeviceData> &devices, qint64 uniqueId) -{ - for (int i = 0; i < devices.size(); ++i) - if (devices.at(i).uniqueId == uniqueId) - return i; - return -1; -} - -static inline QTabletEvent::TabletDevice deviceType(const UINT cursorType) +static inline QInputDevice::DeviceType deviceType(const UINT cursorType) { if (((cursorType & 0x0006) == 0x0002) && ((cursorType & CursorTypeBitMask) != 0x0902)) - return QTabletEvent::Stylus; + return QInputDevice::DeviceType::Stylus; if (cursorType == 0x4020) // Surface Pro 2 tablet device - return QTabletEvent::Stylus; + return QInputDevice::DeviceType::Stylus; switch (cursorType & CursorTypeBitMask) { case 0x0802: - return QTabletEvent::Stylus; + return QInputDevice::DeviceType::Stylus; case 0x0902: - return QTabletEvent::Airbrush; + return QInputDevice::DeviceType::Airbrush; case 0x0004: - return QTabletEvent::FourDMouse; + return QInputDevice::DeviceType::Mouse; case 0x0006: - return QTabletEvent::Puck; + return QInputDevice::DeviceType::Puck; case 0x0804: - return QTabletEvent::RotationStylus; + return QInputDevice::DeviceType::Stylus; default: break; } - return QTabletEvent::NoDevice; + return QInputDevice::DeviceType::Unknown; } -static inline QTabletEvent::PointerType pointerType(unsigned currentCursor) +static inline QPointingDevice::PointerType pointerType(unsigned currentCursor) { switch (currentCursor % 3) { // %3 for dual track case 0: - return QTabletEvent::Cursor; + return QPointingDevice::PointerType::Cursor; case 1: - return QTabletEvent::Pen; + return QPointingDevice::PointerType::Pen; case 2: - return QTabletEvent::Eraser; + return QPointingDevice::PointerType::Eraser; default: break; } - return QTabletEvent::UnknownPointer; + return QPointingDevice::PointerType::Unknown; } -QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(qint64 uniqueId, UINT cursorType) const +inline void QWindowsTabletSupport::enterProximity(ulong time, QWindow *window) +{ + enterLeaveProximity(true, time, window); +} + +inline void QWindowsTabletSupport::leaveProximity(ulong time, QWindow *window) +{ + enterLeaveProximity(false, time, window); +} + +void QWindowsTabletSupport::enterLeaveProximity(bool enter, ulong time, QWindow *window) +{ + Q_ASSERT(!m_currentDevice.isNull()); + if (time == 0) // Some leave events do not have a time associated + ++m_eventTime; + else + m_eventTime = time; + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, m_eventTime, + m_currentDevice.data(), + enter); +} + +QWindowsTabletSupport::DevicePtr QWindowsTabletSupport::findDevice(qint64 systemId) const +{ + for (const auto &d : m_devices) { + if (d->deviceData()->systemId == systemId) + return d; + } + return {}; +} + +QWindowsTabletSupport::DevicePtr QWindowsTabletSupport::findDevice(qint64 systemId, + QInputDevice::DeviceType deviceType, + QPointingDevice::PointerType pointerType) const +{ + for (const auto &d : m_devices) { + if (d->deviceData()->systemId == systemId && d->type() == deviceType + && d->pointerType() == pointerType) { + return d; + } + } + return {}; +} + +// Clone a device for a new pointer type. +QWindowsTabletSupport::DevicePtr QWindowsTabletSupport::clonePhysicalDevice(qint64 systemId, + QInputDevice::DeviceType deviceType, + QPointingDevice::PointerType pointerType) +{ + auto similar = findDevice(systemId); + if (similar.isNull()) + return {}; + DevicePtr result(createInputDevice(similar->deviceData(), deviceType, pointerType)); + m_devices.append(result); + return result; +} + +void QWindowsTabletSupport::updateData(QWindowsTabletDeviceData *data) const { - QWindowsTabletDeviceData result; - result.uniqueId = uniqueId; /* browse WinTab's many info items to discover pressure handling. */ AXIS axis; LOGCONTEXT lc; @@ -377,21 +429,40 @@ QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(qint64 uniqueId, UINT QWindowsTabletSupport::m_winTab32DLL.wTGet(m_context, &lc); /* get the size of the pressure axis. */ QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &axis); - result.minPressure = int(axis.axMin); - result.maxPressure = int(axis.axMax); + data->minPressure = int(axis.axMin); + data->maxPressure = int(axis.axMax); QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &axis); - result.minTanPressure = int(axis.axMin); - result.maxTanPressure = int(axis.axMax); + data->minTanPressure = int(axis.axMin); + data->maxTanPressure = int(axis.axMax); LOGCONTEXT defaultLc; /* get default region */ QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFCONTEXT, 0, &defaultLc); - result.maxX = int(defaultLc.lcInExtX) - int(defaultLc.lcInOrgX); - result.maxY = int(defaultLc.lcInExtY) - int(defaultLc.lcInOrgY); - result.maxZ = int(defaultLc.lcInExtZ) - int(defaultLc.lcInOrgZ); - result.currentDevice = deviceType(cursorType); - return result; + data->maxX = int(defaultLc.lcInExtX) - int(defaultLc.lcInOrgX); + data->maxY = int(defaultLc.lcInExtY) - int(defaultLc.lcInOrgY); + data->maxZ = int(defaultLc.lcInExtZ) - int(defaultLc.lcInOrgZ); +} + +void QWindowsTabletSupport::updateButtons(unsigned currentCursor, QWindowsTabletDeviceData *data) const +{ + // We should check button map for changes on every proximity event, not + // only during initialization phase. + // WARNING: in 2016 there were some Wacom tablet drivers, which could mess up + // button mapping if the remapped button was pressed, while the + // application **didn't have input focus**. This bug is somehow + // related to the fact that Wacom drivers allow user to configure + // per-application button-mappings. If the bug shows up again, + // just move this button-map fetching into initialization block. + // + // See https://bugs.kde.org/show_bug.cgi?id=359561 + BYTE logicalButtons[32]; + memset(logicalButtons, 0, 32); + m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons); + data->buttonsMap.clear(); + data->buttonsMap[0x1] = logicalButtons[0]; + data->buttonsMap[0x2] = logicalButtons[1]; + data->buttonsMap[0x4] = logicalButtons[2]; } bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, LPARAM lParam) @@ -400,21 +471,11 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer); if (!LOWORD(lParam)) { - qCDebug(lcQpaTablet) << "leave proximity for device #" << m_currentDevice; - if (m_currentDevice < 0 || m_currentDevice >= m_devices.size()) // QTBUG-65120, spurious leave observed + if (m_currentDevice.isNull()) // QTBUG-65120, spurious leave observed return false; + qCDebug(lcQpaTablet) << "leave proximity for device #" << m_currentDevice.data(); m_state = PenUp; - if (totalPacks > 0) { - QWindowSystemInterface::handleTabletLeaveProximityEvent(proximityBuffer[0].pkTime, - m_devices.at(m_currentDevice).currentDevice, - m_devices.at(m_currentDevice).currentPointerType, - m_devices.at(m_currentDevice).uniqueId); - } else { - QWindowSystemInterface::handleTabletLeaveProximityEvent(m_devices.at(m_currentDevice).currentDevice, - m_devices.at(m_currentDevice).currentPointerType, - m_devices.at(m_currentDevice).uniqueId); - - } + leaveProximity(totalPacks > 0 ? proximityBuffer[0].pkTime : 0u); return true; } @@ -424,46 +485,37 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L const UINT currentCursor = proximityBuffer[0].pkCursor; UINT physicalCursorId; QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &physicalCursorId); + const qint64 systemId = physicalCursorId; UINT cursorType; QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_TYPE, &cursorType); - const qint64 uniqueId = (qint64(cursorType & DeviceIdMask) << 32L) | qint64(physicalCursorId); + + const QInputDevice::DeviceType currentType = deviceType(cursorType); + const QPointingDevice::PointerType currentPointerType = pointerType(currentCursor); // initializing and updating the cursor should be done in response to // WT_CSRCHANGE. We do it in WT_PROXIMITY because some wintab never send // the event WT_CSRCHANGE even if asked with CXO_CSRMESSAGES - m_currentDevice = indexOfDevice(m_devices, uniqueId); - if (m_currentDevice < 0) { - m_currentDevice = m_devices.size(); - m_devices.push_back(tabletInit(uniqueId, cursorType)); + m_currentDevice = findDevice(systemId, currentType, currentPointerType); + if (m_currentDevice.isNull()) + m_currentDevice = clonePhysicalDevice(systemId, currentType, currentPointerType); + if (m_currentDevice.isNull()) { + QWinTabPointingDevice::DeviceDataPtr data(new QWindowsTabletDeviceData); + data->systemId = systemId; + data->tiltCapability = m_tiltSupport; + data->zCapability = (cursorType == 0x0004); + updateButtons(currentCursor, data.data()); + m_currentDevice.reset(createInputDevice(data, currentType, currentPointerType)); + m_devices.append(m_currentDevice); } - /** - * We should check button map for changes on every proximity event, not - * only during initialization phase. - * - * WARNING: in 2016 there were some Wacom table drivers, which could mess up - * button mapping if the remapped button was pressed, while the - * application **didn't have input focus**. This bug is somehow - * related to the fact that Wacom drivers allow user to configure - * per-application button-mappings. If the bug shows up again, - * just move this button-map fetching into initialization block. - * - * See https://bugs.kde.org/show_bug.cgi?id=359561 - */ - BYTE logicalButtons[32]; - memset(logicalButtons, 0, 32); - m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons); - m_devices[m_currentDevice].buttonsMap[0x1] = logicalButtons[0]; - m_devices[m_currentDevice].buttonsMap[0x2] = logicalButtons[1]; - m_devices[m_currentDevice].buttonsMap[0x4] = logicalButtons[2]; + // The user can switch pressure sensitivity level in the driver,which + // will make our saved values invalid (this option is provided by Wacom + // drivers for compatibility reasons, and it can be adjusted on the fly) + updateData(m_currentDevice->deviceData().data()); - m_devices[m_currentDevice].currentPointerType = pointerType(currentCursor); m_state = PenProximity; qCDebug(lcQpaTablet) << "enter proximity for device #" - << m_currentDevice << m_devices.at(m_currentDevice); - QWindowSystemInterface::handleTabletEnterProximityEvent(proximityBuffer[0].pkTime, - m_devices.at(m_currentDevice).currentDevice, - m_devices.at(m_currentDevice).currentPointerType, - m_devices.at(m_currentDevice).uniqueId); + << m_currentDevice.data(); + enterProximity(proximityBuffer[0].pkTime); return true; } @@ -517,12 +569,10 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() { static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue. const int packetCount = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, TabletPacketQSize, &localPacketBuf); - if (!packetCount || m_currentDevice < 0) + if (!packetCount || m_currentDevice.isNull()) return false; - const int currentDevice = m_devices.at(m_currentDevice).currentDevice; - const int currentPointer = m_devices.at(m_currentDevice).currentPointerType; - const qint64 uniqueId = m_devices.at(m_currentDevice).uniqueId; + const QWindowsTabletDeviceData ¤t = *m_currentDevice->deviceData(); // The tablet can be used in 2 different modes (reflected in enum Mode), // depending on its settings: @@ -542,21 +592,40 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() if (QWindowsContext::verbose > 1) { qCDebug(lcQpaTablet) << __FUNCTION__ << "processing" << packetCount - << "mode=" << m_mode << "target:" - << QGuiApplicationPrivate::tabletDevicePoint(uniqueId).target; + << "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]; - const int z = currentDevice == QTabletEvent::FourDMouse ? int(packet.pkZ) : 0; + const int z = current.zCapability ? int(packet.pkZ) : 0; + + const auto packetPointerType = pointerType(packet.pkCursor); + + const Qt::MouseButtons buttons = + convertTabletButtons(packet.pkButtons, current); + + if (buttons == Qt::NoButton && packetPointerType != m_currentDevice->pointerType()) { + leaveProximity(packet.pkTime); + Q_ASSERT(!m_currentDevice.isNull()); + // Pointer type changed, find or clone a new device for this physical cursor. + const qint64 systemId = m_currentDevice->systemId(); + const QInputDevice::DeviceType type = m_currentDevice->type(); + m_currentDevice = findDevice(systemId, type, packetPointerType); + if (m_currentDevice.isNull()) + m_currentDevice = clonePhysicalDevice(systemId, type, packetPointerType); + Q_ASSERT(!m_currentDevice.isNull()); + enterProximity(packet.pkTime); + } QPointF globalPosF = - m_devices.at(m_currentDevice).scaleCoordinates(packet.pkX, packet.pkY, virtualDesktopArea); + current.scaleCoordinates(packet.pkX, packet.pkY, virtualDesktopArea); - QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(uniqueId).target; // Pass to window that grabbed it. + // Pass to window that grabbed it. + QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(m_currentDevice->uniqueId().numericId()).target; // Get Mouse Position and compare to tablet info const QPoint mouseLocation = QWindowsCursor::mousePosition(); @@ -580,12 +649,12 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() Q_ASSERT(platformWindow); const QPoint localPos = platformWindow->mapFromGlobal(globalPos); - const qreal pressureNew = packet.pkButtons && (currentPointer == QTabletEvent::Pen || currentPointer == QTabletEvent::Eraser) ? - m_devices.at(m_currentDevice).scalePressure(packet.pkNormalPressure) : - qreal(0); - const qreal tangentialPressure = currentDevice == QTabletEvent::Airbrush ? - m_devices.at(m_currentDevice).scaleTangentialPressure(packet.pkTangentPressure) : - qreal(0); + const qreal pressureNew = packet.pkButtons + && (m_currentDevice->pointerType() == QPointingDevice::PointerType::Pen + || m_currentDevice->pointerType() == QPointingDevice::PointerType::Eraser) + ? current.scalePressure(packet.pkNormalPressure) : qreal(0); + const qreal tangentialPressure = m_currentDevice->type() == QInputDevice::DeviceType::Airbrush + ? current.scaleTangentialPressure(packet.pkTangentPressure) : qreal(0); int tiltX = 0; int tiltY = 0; @@ -614,20 +683,18 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() qCDebug(lcQpaTablet) << "Packet #" << i << '/' << packetCount << "button:" << packet.pkButtons << globalPosF << z << "to:" << target << localPos << "(packet" << packet.pkX - << packet.pkY << ") dev:" << currentDevice << "pointer:" - << currentPointer << "P:" << pressureNew << "tilt:" << tiltX << ',' - << tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation; + << packet.pkY << ") dev:" << m_currentDevice->type() << "pointer:" + << m_currentDevice->pointerType() << "P:" << pressureNew << "tilt:" << tiltX << ',' + << tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation + << " target=" << target; } - Qt::MouseButtons buttons = - convertTabletButtons(packet.pkButtons, m_devices.at(m_currentDevice)); - - QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, QPointF(localPos), globalPosF, - currentDevice, currentPointer, - buttons, - pressureNew, tiltX, tiltY, + m_eventTime = packet.pkTime; + QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, + m_currentDevice.data(), + QPointF(localPos), globalPosF, + buttons, pressureNew, tiltX, tiltY, tangentialPressure, rotation, z, - uniqueId, keyboardModifiers); } return true; diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.h b/src/plugins/platforms/windows/qwindowstabletsupport.h index 6bcf3357a5..fb639294d3 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.h +++ b/src/plugins/platforms/windows/qwindowstabletsupport.h @@ -1,51 +1,17 @@ -/**************************************************************************** -** -** 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 #include "qtwindowsglobal.h" #include <QtGui/qtguiglobal.h> +#include <QtGui/qpointingdevice.h> -#include <QtCore/qvector.h> -#include <QtCore/qpoint.h> #include <QtCore/qhash.h> +#include <QtCore/qlist.h> +#include <QtCore/qpoint.h> +#include <QtCore/qsharedpointer.h> #include <wintab.h> @@ -82,6 +48,8 @@ struct QWindowsWinTab32DLL PtrWTQueueSizeSet wTQueueSizeSet = nullptr; }; +// Data associated with a physical cursor (system ID) which is shared between +// devices of varying device type/pointer type. struct QWindowsTabletDeviceData { QPointF scaleCoordinates(int coordX, int coordY,const QRect &targetArea) const; @@ -98,12 +66,31 @@ struct QWindowsTabletDeviceData int maxY = 0; int minZ = 0; int maxZ = 0; - qint64 uniqueId = 0; - int currentDevice = 0; - int currentPointerType = 0; + qint64 systemId = 0; + bool zCapability = false; + bool tiltCapability = false; QHash<quint8, quint8> buttonsMap; }; +class QWinTabPointingDevice : public QPointingDevice +{ +public: + using DeviceDataPtr = QSharedPointer<QWindowsTabletDeviceData>; + + explicit QWinTabPointingDevice(const DeviceDataPtr &data, + const QString &name, qint64 systemId, + QInputDevice::DeviceType devType, + PointerType pType, Capabilities caps, int maxPoints, + int buttonCount, const QString &seatName = QString(), + QPointingDeviceUniqueId uniqueId = QPointingDeviceUniqueId(), + QObject *parent = nullptr); + + const DeviceDataPtr &deviceData() const { return m_deviceData; } + +private: + DeviceDataPtr m_deviceData; +}; + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t); #endif @@ -115,6 +102,9 @@ class QWindowsTabletSupport explicit QWindowsTabletSupport(HWND window, HCTX context); public: + using DevicePtr = QSharedPointer<QWinTabPointingDevice>; + using Devices = QList<DevicePtr>; + enum Mode { PenMode, @@ -138,22 +128,35 @@ public: bool translateTabletProximityEvent(WPARAM wParam, LPARAM lParam); bool translateTabletPacketEvent(); - int absoluteRange() const { return m_absoluteRange; } - void setAbsoluteRange(int a) { m_absoluteRange = a; } + static int absoluteRange() { return m_absoluteRange; } + static void setAbsoluteRange(int a) { m_absoluteRange = a; } private: unsigned options() const; QWindowsTabletDeviceData tabletInit(qint64 uniqueId, UINT cursorType) const; + void updateData(QWindowsTabletDeviceData *data) const; + void updateButtons(unsigned currentCursor, QWindowsTabletDeviceData *data) const; + void enterProximity(ulong time = 0, QWindow *window = nullptr); + void leaveProximity(ulong time = 0, QWindow *window = nullptr); + void enterLeaveProximity(bool enter, ulong time, QWindow *window = nullptr); + DevicePtr findDevice(qint64 systemId) const; + DevicePtr findDevice(qint64 systemId, + QInputDevice::DeviceType deviceType, + QPointingDevice::PointerType pointerType) const; + DevicePtr clonePhysicalDevice(qint64 systemId, + QInputDevice::DeviceType deviceType, + QPointingDevice::PointerType pointerType); static QWindowsWinTab32DLL m_winTab32DLL; const HWND m_window; const HCTX m_context; - int m_absoluteRange = 20; + static int m_absoluteRange; bool m_tiltSupport = false; - QVector<QWindowsTabletDeviceData> m_devices; - int m_currentDevice = -1; + Devices m_devices; + DevicePtr m_currentDevice; Mode m_mode = PenMode; State m_state = PenUp; + ulong m_eventTime = 0; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 32a65109af..e8a324aedb 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -1,107 +1,59 @@ -/**************************************************************************** -** -** 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 "qt_windows.h" +#include "qwindowsscreen.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/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> #include <QtGui/qpainter.h> #include <QtGui/qpixmapcache.h> #include <qpa/qwindowsysteminterface.h> -#include <QtThemeSupport/private/qabstractfileiconengine_p.h> -#include <QtFontDatabaseSupport/private/qwindowsfontdatabase_p.h> +#include <QtGui/private/qabstractfileiconengine_p.h> +#include <QtGui/private/qwindowsfontdatabase_p.h> #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> + +# include <winrt/Windows.UI.ViewManagement.h> +#endif // QT_CONFIG(cpp_winrt) QT_BEGIN_NAMESPACE -static inline QColor COLORREFToQColor(COLORREF cr) -{ - return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); -} - -static inline QTextStream& operator<<(QTextStream &str, const QColor &c) -{ - str.setIntegerBase(16); - str.setFieldWidth(2); - str.setPadChar(QLatin1Char('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) { @@ -126,120 +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 { - connect(this, &QThread::finished, this, &QObject::deleteLater); + 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() + { + start(); } - void run() override + ~QShGetFileInfoThread() { - m_init = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + cancel(); + wait(); + } - QMutexLocker readyLocker(&m_readyMutex); - while (!m_cancelled.loadRelaxed()) { - if (!m_params && !m_cancelled.loadRelaxed() - && !m_readyCondition.wait(&m_readyMutex, QDeadlineTimer(1000ll))) - continue; + QSharedPointer<Task> getNextTask() + { + QMutexLocker l(&m_waitForTaskMutex); + while (!isInterruptionRequested()) { + if (!m_taskQueue.isEmpty()) + return m_taskQueue.dequeue(); + m_waitForTaskCondition.wait(&m_waitForTaskMutex); + } + return nullptr; + } - if (m_params) { - const QString fileName = m_params->fileName; + void run() override + { + QComHelper comHelper(COINIT_MULTITHREADED); + + 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; -} +Q_APPLICATION_STATIC(QShGetFileInfoThread, s_shGetFileInfoThread) // from QStyle::standardPalette static inline QPalette standardPalette() @@ -258,25 +249,47 @@ static inline QPalette standardPalette() return palette; } -static inline QPalette systemPalette() +static QColor placeHolderColor(QColor textColor) { - QPalette result = standardPalette(); + textColor.setAlpha(128); + return textColor; +} + +/* + 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)); - result.setColor(QPalette::Button, getSysColor(COLOR_BTNFACE)); - result.setColor(QPalette::Light, getSysColor(COLOR_BTNHIGHLIGHT)); + result.setColor(QPalette::Button, btnFace); + result.setColor(QPalette::Light, btnHighlight); result.setColor(QPalette::Dark, getSysColor(COLOR_BTNSHADOW)); result.setColor(QPalette::Mid, result.button().color().darker(150)); - result.setColor(QPalette::Text, getSysColor(COLOR_WINDOWTEXT)); - result.setColor(QPalette::BrightText, getSysColor(COLOR_BTNHIGHLIGHT)); + result.setColor(QPalette::Text, textColor); + result.setColor(QPalette::PlaceholderText, placeHolderColor(textColor)); + result.setColor(QPalette::BrightText, btnHighlight); result.setColor(QPalette::Base, getSysColor(COLOR_WINDOW)); - result.setColor(QPalette::Window, getSysColor(COLOR_BTNFACE)); + result.setColor(QPalette::Window, btnFace); 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::Link, Qt::blue); - result.setColor(QPalette::LinkVisited, Qt::magenta); + result.setColor(QPalette::Accent, accent); + + 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()); @@ -284,35 +297,79 @@ static inline QPalette systemPalette() 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, - getSysColor(COLOR_HIGHLIGHT)); - result.setColor(QPalette::Disabled, QPalette::HighlightedText, - getSysColor(COLOR_HIGHLIGHTTEXT)); - 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) +static inline QPalette toolTipPalette(const QPalette &systemPalette, bool light) { QPalette result(systemPalette); - const QColor tipBgColor(getSysColor(COLOR_INFOBK)); - const QColor tipTextColor(getSysColor(COLOR_INFOTEXT)); + 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); @@ -326,8 +383,7 @@ static inline QPalette toolTipPalette(const QPalette &systemPalette) 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); @@ -337,12 +393,15 @@ static inline QPalette toolTipPalette(const QPalette &systemPalette) return result; } -static inline QPalette menuPalette(const QPalette &systemPalette) +static inline QPalette menuPalette(const QPalette &systemPalette, bool light) { + if (!light) + return systemPalette; + QPalette result(systemPalette); - const QColor menuColor(getSysColor(COLOR_MENU)); - const QColor menuTextColor(getSysColor(COLOR_MENUTEXT)); - const QColor disabled(getSysColor(COLOR_GRAYTEXT)); + 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); @@ -351,8 +410,8 @@ static inline QPalette menuPalette(const QPalette &systemPalette) result.setColor(QPalette::Disabled, QPalette::WindowText, disabled); result.setColor(QPalette::Disabled, QPalette::Text, disabled); const bool isFlat = booleanSystemParametersInfo(SPI_GETFLATMENU, false); - result.setColor(QPalette::Disabled, QPalette::Highlight, - getSysColor(isFlat ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT)); + 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, result.color(QPalette::Active, QPalette::Button)); @@ -373,16 +432,17 @@ static inline QPalette menuPalette(const QPalette &systemPalette) return result; } -static inline QPalette *menuBarPalette(const QPalette &menuPalette) +static inline QPalette *menuBarPalette(const QPalette &menuPalette, bool light) { QPalette *result = nullptr; - if (booleanSystemParametersInfo(SPI_GETFLATMENU, false)) { - 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); - } + 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; } @@ -392,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(); @@ -407,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() @@ -468,12 +532,43 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const } case MouseDoubleClickDistance: return GetSystemMetrics(SM_CXDOUBLECLK); + case MenuBarFocusOnAltPressRelease: + return true; default: break; } return QPlatformTheme::themeHint(hint); } +Qt::ColorScheme QWindowsTheme::colorScheme() const +{ + return QWindowsTheme::effectiveColorScheme(); +} + +Qt::ColorScheme QWindowsTheme::effectiveColorScheme() +{ + if (queryHighContrast()) + return Qt::ColorScheme::Unknown; + return s_colorScheme; +} + +void QWindowsTheme::handleSettingsChanged() +{ + 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() { qDeleteAll(m_palettes, m_palettes + NPalettes); @@ -482,13 +577,64 @@ void QWindowsTheme::clearPalettes() void QWindowsTheme::refreshPalettes() { - if (!QGuiApplication::desktopSettingsAware()) return; - m_palettes[SystemPalette] = new QPalette(systemPalette()); - m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette])); - m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette])); - m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette]); + const bool light = + effectiveColorScheme() != Qt::ColorScheme::Dark + || !QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle); + 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[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]); + 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() @@ -497,23 +643,55 @@ void QWindowsTheme::clearFonts() std::fill(m_fonts, m_fonts + NFonts, nullptr); } +void QWindowsTheme::refresh() +{ + refreshPalettes(); + refreshFonts(); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const NONCLIENTMETRICS &m) +{ + QDebugStateSaver saver(d); + d.nospace(); + d.noquote(); + d << "NONCLIENTMETRICS(iMenu=" << m.iMenuWidth << 'x' << m.iMenuHeight + << ", lfCaptionFont="; + QWindowsFontDatabase::debugFormat(d, m.lfCaptionFont); + d << ", lfSmCaptionFont="; + QWindowsFontDatabase::debugFormat(d, m.lfSmCaptionFont); + d << ", lfMenuFont="; + QWindowsFontDatabase::debugFormat(d, m.lfMenuFont); + d << ", lfMessageFont="; + QWindowsFontDatabase::debugFormat(d, m.lfMessageFont); + d <<", lfStatusFont="; + QWindowsFontDatabase::debugFormat(d, m.lfStatusFont); + d << ')'; + return d; +} +#endif // QT_NO_DEBUG_STREAM + void QWindowsTheme::refreshFonts() { clearFonts(); if (!QGuiApplication::desktopSettingsAware()) return; + + const int dpi = 96; NONCLIENTMETRICS ncm; - QWindowsContext::nonClientMetrics(&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); @@ -570,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 @@ -687,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; + } } } @@ -729,15 +906,15 @@ 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 += QLatin1Char('l'); + key += u'l'; switch (imageListSize) { case sHIL_EXTRALARGE: - key += QLatin1Char('e'); + key += u'e'; break; case sHIL_JUMBO: - key += QLatin1Char('j'); + key += u'j'; break; } return key; @@ -748,7 +925,7 @@ class FakePointer { public: - Q_STATIC_ASSERT_X(sizeof(T) <= sizeof(void *), "FakePointers can only go that far."); + static_assert(sizeof(T) <= sizeof(void *), "FakePointers can only go that far."); static FakePointer *create(T thing) { @@ -765,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}}; @@ -777,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; } @@ -796,7 +968,7 @@ public: explicit QWindowsFileIconEngine(const QFileInfo &info, QPlatformTheme::IconOptions opts) : QAbstractFileIconEngine(info, opts) {} - QList<QSize> availableSizes(QIcon::Mode = QIcon::Normal, QIcon::State = QIcon::Off) const override + QList<QSize> availableSizes(QIcon::Mode = QIcon::Normal, QIcon::State = QIcon::Off) override { return QWindowsTheme::instance()->availableFileIconSizes(); } protected: @@ -815,24 +987,21 @@ QString QWindowsFileIconEngine::cacheKey() const // It is faster to just look at the file extensions; // avoiding slow QFileInfo::isExecutable() (QTBUG-13182) QString suffix = fileInfo().suffix(); - if (!suffix.compare(QLatin1String("exe"), Qt::CaseInsensitive) - || !suffix.compare(QLatin1String("lnk"), Qt::CaseInsensitive) - || !suffix.compare(QLatin1String("ico"), Qt::CaseInsensitive)) { + if (!suffix.compare(u"exe", Qt::CaseInsensitive) + || !suffix.compare(u"lnk", Qt::CaseInsensitive) + || !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; @@ -841,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); @@ -863,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; @@ -875,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; @@ -922,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(); @@ -946,6 +1115,25 @@ bool QWindowsTheme::useNativeMenus() return result; } +Qt::ColorScheme QWindowsTheme::queryColorScheme() +{ + 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 ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; +} + +bool QWindowsTheme::queryHighContrast() +{ + 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 { qCDebug(lcQpaMenus) << __FUNCTION__; diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h index 7a8c321da4..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 @@ -64,6 +30,11 @@ public: QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override; #endif QVariant themeHint(ThemeHint) const override; + + Qt::ColorScheme colorScheme() const override; + + static void handleSettingsChanged(); + const QPalette *palette(Palette type = SystemPalette) const override { return m_palettes[type]; } const QFont *font(Font type = SystemFont) const override @@ -72,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(); } @@ -86,17 +58,27 @@ public: static bool useNativeMenus(); void refreshFonts(); + void refresh(); static const char *name; + static QPalette systemPalette(Qt::ColorScheme); + private: - void refresh() { refreshPalettes(); refreshFonts(); } 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 ffe2e62069..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 @@ -55,7 +19,6 @@ QT_BEGIN_NAMESPACE to be done by using QWaitCondition/QMutex. \internal - \ingroup qt-lighthouse-win */ class QWindowsThreadPoolRunner { 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 cc7ef476d4..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 @@ -46,7 +10,7 @@ #define VK_USE_PLATFORM_WIN32_KHR -#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h> +#include <QtGui/private/qbasicvulkanplatforminstance_p.h> #include <QtCore/qlibrary.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index c3b7a92df9..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; } @@ -253,7 +338,7 @@ QDebug operator<<(QDebug d, const GUID &guid) { QDebugStateSaver saver(d); d.nospace(); - d << '{' << Qt::hex << Qt::uppercasedigits << qSetPadChar(QLatin1Char('0')) + d << '{' << Qt::hex << Qt::uppercasedigits << qSetPadChar(u'0') << qSetFieldWidth(8) << guid.Data1 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4) << guid.Data2 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4) @@ -346,10 +431,8 @@ 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: + case QSurface::Direct3DSurface: return true; default: return false; @@ -358,20 +441,11 @@ static inline bool windowIsAccelerated(const QWindow *w) static bool applyBlurBehindWindow(HWND hwnd) { - BOOL compositionEnabled; - if (DwmIsCompositionEnabled(&compositionEnabled) != S_OK) - return false; - DWM_BLURBEHIND blurBehind = {0, 0, nullptr, 0}; - if (compositionEnabled) { - blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - blurBehind.fEnable = TRUE; - blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1); - } else { - blurBehind.dwFlags = DWM_BB_ENABLE; - blurBehind.fEnable = FALSE; - } + blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + blurBehind.fEnable = TRUE; + blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1); const bool result = DwmEnableBlurBehindWindow(hwnd, &blurBehind) == S_OK; @@ -392,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; @@ -415,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); @@ -438,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. @@ -489,7 +580,6 @@ static QMargins invisibleMargins(QPoint screenPoint) \sa QWindowCreationContext \internal - \ingroup qt-lighthouse-win */ struct WindowCreationData @@ -553,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; } } @@ -570,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 @@ -703,52 +798,70 @@ 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) +{ + 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 WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const { @@ -777,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 @@ -807,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; @@ -816,6 +930,9 @@ QWindowsWindowData return result; } + if (QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark && shouldApplyDarkFrame(w)) + QWindowsWindow::setDarkBorderToWindow(result.hwnd, true); + if (mirrorParentWidth != 0) { context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width() - context->obtainedPos.x()); @@ -824,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; @@ -844,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 " @@ -898,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. @@ -906,79 +1039,86 @@ static QSize toNativeSizeConstrained(QSize dip, const QScreen *s) into account. \internal - \ingroup qt-lighthouse-win */ -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) @@ -994,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; @@ -1030,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 @@ -1045,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, @@ -1076,12 +1216,11 @@ bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) \since 5.6 \internal - \ingroup qt-lighthouse-win */ 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) @@ -1117,7 +1256,21 @@ 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> + QWindowsBaseWindow::touchWindowTouchTypes_sys() const +{ + ULONG touchFlags = 0; + if (IsTouchWindow(handle(), &touchFlags) == FALSE) + return {}; + TouchWindowTouchTypes result; + if ((touchFlags & TWF_FINETOUCH) != 0) + result.setFlag(TouchWindowTouchType::FineTouch); + if ((touchFlags & TWF_WANTPALM) != 0) + result.setFlag(TouchWindowTouchType::WantPalmTouch); + return result; } void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other windows. @@ -1128,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 @@ -1139,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())); } @@ -1160,12 +1313,32 @@ QPoint QWindowsBaseWindow::mapFromGlobal(const QPoint &pos) const return QWindowsGeometryHint::mapFromGlobal(handle(), pos); } +void QWindowsBaseWindow::setHasBorderInFullScreen(bool) +{ + Q_UNIMPLEMENTED(); +} + +bool QWindowsBaseWindow::hasBorderInFullScreen() const +{ + Q_UNIMPLEMENTED(); + return false; +} + +QMargins QWindowsBaseWindow::customMargins() const +{ + return {}; +} + +void QWindowsBaseWindow::setCustomMargins(const QMargins &) +{ + Q_UNIMPLEMENTED(); +} + /*! \class QWindowsDesktopWindow \brief Window wrapping GetDesktopWindow not allowing any manipulation. \since 5.6 \internal - \ingroup qt-lighthouse-win */ /*! @@ -1178,7 +1351,6 @@ QPoint QWindowsBaseWindow::mapFromGlobal(const QPoint &pos) const \since 5.6 \internal - \ingroup qt-lighthouse-win */ QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd) @@ -1186,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) @@ -1194,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. @@ -1212,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 @@ -1237,7 +1411,6 @@ void QWindowsForeignWindow::setVisible(bool visible) \sa WindowCreationData, QWindowsContext \internal - \ingroup qt-lighthouse-win */ QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QScreen *s, @@ -1250,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(); @@ -1275,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 << '+' @@ -1295,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). @@ -1307,18 +1483,17 @@ void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const \endlist \internal - \ingroup qt-lighthouse-win */ 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), m_data(data), - m_cursor(new CursorHandle), - m_format(aWindow->requestedFormat()) + m_cursor(new CursorHandle) #if QT_CONFIG(vulkan) , m_vkSurface(VK_NULL_HANDLE) #endif @@ -1327,21 +1502,23 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) const Qt::WindowType type = aWindow->type(); if (type == Qt::Desktop) return; // No further handling for Qt::Desktop -#ifndef QT_NO_OPENGL - if (aWindow->surfaceType() == QWindow::OpenGLSurface) { - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) - setFlag(OpenGLSurface); - else - setFlag(OpenGL_ES2); - } -#endif // QT_NO_OPENGL + if (aWindow->surfaceType() == QWindow::Direct3DSurface) + setFlag(Direct3DSurface); +#if QT_CONFIG(opengl) + if (aWindow->surfaceType() == QWindow::OpenGLSurface) + setFlag(OpenGLSurface); +#endif #if QT_CONFIG(vulkan) if (aWindow->surfaceType() == QSurface::VulkanSurface) setFlag(VulkanSurface); #endif updateDropSite(window()->isTopLevel()); - registerTouchWindow(); + // Register touch unless if the flags are already set by a hook + // such as HCBT_CREATEWND + if (!touchWindowTouchTypes_sys().has_value()) + registerTouchWindow(); + const qreal opacity = qt_window_private(aWindow)->opacity; if (!qFuzzyCompare(opacity, qreal(1.0))) setOpacity(opacity); @@ -1358,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(); @@ -1378,14 +1556,20 @@ 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(GetDpiForWindow(handle())); +} + +QSurfaceFormat QWindowsWindow::format() const +{ + return window()->requestedFormat(); } void QWindowsWindow::fireExpose(const QRegion ®ion, bool force) @@ -1397,9 +1581,14 @@ void QWindowsWindow::fireExpose(const QRegion ®ion, bool force) QWindowSystemInterface::handleExposeEvent(window(), region); } +void QWindowsWindow::fireFullExpose(bool force) +{ + fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), 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) @@ -1506,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. @@ -1548,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(); @@ -1557,7 +1746,7 @@ void QWindowsWindow::setVisible(bool visible) // over the rendering of the window // There is nobody waiting for this, so we don't need to flush afterwards. if (isLayered()) - fireExpose(QRect(0, 0, win->width(), win->height())); + fireFullExpose(); // QTBUG-44928, QTBUG-7386: This is to resolve the problem where popups are // opened from the system tray and not being implicitly activated @@ -1728,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); @@ -1779,12 +1968,110 @@ void QWindowsWindow::handleHidden() void QWindowsWindow::handleCompositionSettingsChanged() { const QWindow *w = window(); - if ((w->surfaceType() == QWindow::OpenGLSurface || w->surfaceType() == QWindow::VulkanSurface) - && w->format().hasAlpha()) { + if ((w->surfaceType() == QWindow::OpenGLSurface + || w->surfaceType() == QWindow::VulkanSurface + || w->surfaceType() == QWindow::Direct3DSurface) + && w->format().hasAlpha()) + { applyBlurBehindWindow(handle()); } } +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; @@ -1803,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; } @@ -1855,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). @@ -1862,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), @@ -1874,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 { @@ -1889,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: @@ -1900,12 +2226,13 @@ void QWindowsWindow::handleResized(int wParam) handleWindowStateChange(m_windowState | Qt::WindowMinimized); return; case SIZE_MAXIMIZED: + handleGeometryChange(); if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry)) handleWindowStateChange(Qt::WindowMaximized | (isFullScreen_sys() ? Qt::WindowFullScreen : Qt::WindowNoState)); - handleGeometryChange(); break; case SIZE_RESTORED: + handleGeometryChange(); if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry)) { if (isFullScreen_sys()) handleWindowStateChange( @@ -1914,7 +2241,6 @@ void QWindowsWindow::handleResized(int wParam) else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen)) handleWindowStateChange(Qt::WindowNoState); } - handleGeometryChange(); break; } } @@ -1926,44 +2252,43 @@ 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 - if (mode == FromGeometryChange - && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) { + // Check on currentScreen as it can be 0 when resuming a session (QTBUG-80436). + const bool changingDpi = !equalDpi(QDpi(savedDpi(), savedDpi()), newScreen->logicalDpi()); + if (mode == FromGeometryChange && currentScreen != nullptr && changingDpi) return; - } - qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ - << ' ' << window() << " \"" << currentScreen->name() + + qCDebug(lcQpaWindow).noquote().nospace() << __FUNCTION__ + << ' ' << window() << " \"" << (currentScreen ? currentScreen->name() : QString()) << "\"->\"" << newScreen->name() << '"'; - if (mode == FromGeometryChange) - setFlag(SynchronousGeometryChangeEvent); updateFullFrameMargins(); - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); + QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(window(), newScreen->screen()); } void QWindowsWindow::handleGeometryChange() { const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); - if (testFlag(WithinDpiChanged) - && QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd) != screen()) { - return; // QGuiApplication will send resize when screen actually changes - } + updateFullFrameMargins(); QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); - // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive - // expose events when shrinking, synthesize. - if (!testFlag(OpenGL_ES2) && isExposed() + // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE + // which we no longer support in Qt 6) do not receive expose + // events when shrinking, synthesize. + if (isExposed() && m_data.geometry.size() != previousGeometry.size() // Exclude plain move // One dimension grew -> Windows will send expose, no need to synthesize. && !(m_data.geometry.width() > previousGeometry.width() || m_data.geometry.height() > previousGeometry.height())) { - fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), true); + fireFullExpose(true); } const bool wasSync = testFlag(SynchronousGeometryChangeEvent); @@ -1972,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; @@ -1982,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; @@ -2013,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(); } @@ -2035,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() @@ -2049,12 +2377,6 @@ void QWindowsWindow::releaseDC() } } -static inline bool dwmIsCompositionEnabled() -{ - BOOL dWmCompositionEnabled = FALSE; - return SUCCEEDED(DwmIsCompositionEnabled(&dWmCompositionEnabled)) && dWmCompositionEnabled == TRUE; -} - static inline bool isSoftwareGl() { #if QT_CONFIG(dynamicgl) @@ -2066,38 +2388,32 @@ 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 - RECT updateRect; - if (!GetUpdateRect(m_data.hwnd, &updateRect, FALSE)) + if (!GetUpdateRect(m_data.hwnd, 0, FALSE)) return false; PAINTSTRUCT ps; - // GL software rendering (QTBUG-58178) and Windows 7/Aero off with some AMD cards + // GL software rendering (QTBUG-58178) with some AMD cards // (QTBUG-60527) need InvalidateRect() to suppress artifacts while resizing. - if (testFlag(OpenGLSurface) && (isSoftwareGl() || !dwmIsCompositionEnabled())) + if (testFlag(OpenGLSurface) && isSoftwareGl()) InvalidateRect(hwnd, nullptr, false); BeginPaint(hwnd, &ps); - // Observed painting problems with Aero style disabled (QTBUG-7865). - if (Q_UNLIKELY(!dwmIsCompositionEnabled()) - && ((testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered)) || testFlag(VulkanSurface))) - { - SelectClipRgn(ps.hdc, nullptr); - } - // If the a window is obscured by another window (such as a child window) // we still need to send isExposed=true, for compatibility. // Our tests depend on it. fireExpose(QRegion(qrectFromRECT(ps.rcPaint)), true); - if (qSizeOfRect(updateRect) == m_data.geometry.size() && !QWindowsContext::instance()->asyncExpose()) + if (!QWindowsContext::instance()->asyncExpose()) QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); EndPaint(hwnd, &ps); @@ -2111,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) { @@ -2129,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; } @@ -2144,12 +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)) + && !(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); @@ -2157,12 +2475,20 @@ 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(); bool exposeEventsSent = false; if (isLayered()) { - fireExpose(QRegion(0, 0, w->width(), w->height())); + fireFullExpose(); exposeEventsSent = true; } const QWindowList allWindows = QGuiApplication::allWindows(); @@ -2170,7 +2496,7 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state) if (child != w && child->isVisible() && child->transientParent() == w) { QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(child); if (platformWindow && platformWindow->isLayered()) { - platformWindow->fireExpose(QRegion(0, 0, child->width(), child->height())); + platformWindow->fireFullExpose(); exposeEventsSent = true; } } @@ -2180,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) { @@ -2216,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(); @@ -2224,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. @@ -2251,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 { @@ -2284,8 +2611,10 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState) if (!screen) screen = QGuiApplication::primaryScreen(); // That area of the virtual desktop might not be covered by a screen anymore. - if (!screen->geometry().intersects(m_savedFrameGeometry)) - m_savedFrameGeometry.moveTo(screen->geometry().topLeft()); + if (const auto platformScreen = screen->handle()) { + if (!platformScreen->geometry().intersects(m_savedFrameGeometry)) + m_savedFrameGeometry.moveTo(platformScreen->geometry().topLeft()); + } if (newState & Qt::WindowMinimized) { setMinimizedGeometry(m_data.hwnd, m_savedFrameGeometry); @@ -2340,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); @@ -2353,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); @@ -2380,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(); @@ -2398,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; @@ -2424,42 +2764,90 @@ 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; } } void QWindowsWindow::updateFullFrameMargins() { - // Normally obtained from WM_NCCALCSIZE + // QTBUG-82580: If a native menu is present, force a WM_NCCALCSIZE. + if (GetMenu(m_data.hwnd)) + QWindowsContext::forceNcCalcSize(m_data.hwnd); + else + calculateFullFrameMargins(); +} + +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) setWindowOpacity(m_data.hwnd, m_data.flags, - window()->format().hasAlpha(), testFlag(OpenGLSurface) || testFlag(VulkanSurface), + window()->format().hasAlpha(), testFlag(OpenGLSurface) || testFlag(VulkanSurface) || testFlag(Direct3DSurface), level); } } @@ -2514,40 +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. - if (QGuiApplication::applicationState() != Qt::ApplicationActive - && QWindowsNativeInterface::windowActivationBehavior() == QWindowsWindowFunctions::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) @@ -2556,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) { @@ -2570,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; @@ -2593,37 +3017,41 @@ bool QWindowsWindow::setMouseGrabEnabled(bool grab) return grab; } -static inline DWORD cornerToWinOrientation(Qt::Corner corner) +static inline DWORD edgesToWinOrientation(Qt::Edges edges) { - switch (corner) { - case Qt::TopLeftCorner: - return 0xf004; // SZ_SIZETOPLEFT; - case Qt::TopRightCorner: - return 0xf005; // SZ_SIZETOPRIGHT - case Qt::BottomLeftCorner: - return 0xf007; // SZ_SIZEBOTTOMLEFT - case Qt::BottomRightCorner: - return 0xf008; // SZ_SIZEBOTTOMRIGHT - } - return 0; + if (edges == Qt::LeftEdge) + return 0xf001; // SC_SIZELEFT; + else if (edges == (Qt::RightEdge)) + return 0xf002; // SC_SIZERIGHT + else if (edges == (Qt::TopEdge)) + return 0xf003; // SC_SIZETOP + else if (edges == (Qt::TopEdge | Qt::LeftEdge)) + return 0xf004; // SC_SIZETOPLEFT + else if (edges == (Qt::TopEdge | Qt::RightEdge)) + return 0xf005; // SC_SIZETOPRIGHT + else if (edges == (Qt::BottomEdge)) + return 0xf006; // SC_SIZEBOTTOM + else if (edges == (Qt::BottomEdge | Qt::LeftEdge)) + return 0xf007; // SC_SIZEBOTTOMLEFT + else if (edges == (Qt::BottomEdge | Qt::RightEdge)) + return 0xf008; // SC_SIZEBOTTOMRIGHT + + return 0xf000; // SC_SIZE } -bool QWindowsWindow::startSystemResize(const QPoint &, Qt::Corner corner) +bool QWindowsWindow::startSystemResize(Qt::Edges edges) { - if (!GetSystemMenu(m_data.hwnd, FALSE)) + if (Q_UNLIKELY(window()->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint))) return false; ReleaseCapture(); - PostMessage(m_data.hwnd, WM_SYSCOMMAND, cornerToWinOrientation(corner), 0); + PostMessage(m_data.hwnd, WM_SYSCOMMAND, edgesToWinOrientation(edges), 0); setFlag(SizeGripOperation); return true; } -bool QWindowsWindow::startSystemMove(const QPoint &) +bool QWindowsWindow::startSystemMove() { - if (!GetSystemMenu(m_data.hwnd, FALSE)) - return false; - ReleaseCapture(); PostMessage(m_data.hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0); return true; @@ -2638,52 +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 { - // We don't apply the min/max size hint as we change the dpi, because we did not adjust the - // QScreen of the window yet so we don't have the min/max with the right ratio - if (!testFlag(QWindowsWindow::WithinDpiChanged)) - 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; + QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi); + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << *mmi; } bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *result) const @@ -2712,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; @@ -2791,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) @@ -2862,7 +3250,8 @@ void QWindowsWindow::setEnabled(bool enabled) static HICON createHIcon(const QIcon &icon, int xSize, int ySize) { if (!icon.isNull()) { - const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize))); + // QTBUG-90363, request DPR=1 for the title bar. + const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize)), 1); if (!pm.isNull()) return qt_pixmapToWinHICON(pm); } @@ -2892,6 +3281,30 @@ bool QWindowsWindow::isTopLevel() const return window()->isTopLevel() && !m_data.embedded; } +enum : WORD { + DwmwaUseImmersiveDarkMode = 20, + DwmwaUseImmersiveDarkModeBefore20h1 = 19 +}; + +bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d) +{ + const BOOL darkBorder = d ? TRUE : FALSE; + const bool ok = + SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &darkBorder, sizeof(darkBorder))) + || SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &darkBorder, sizeof(darkBorder))); + if (!ok) + qCWarning(lcQpaWindow, "%s: Unable to set %s window border.", __FUNCTION__, d ? "dark" : "light"); + return ok; +} + +void QWindowsWindow::setDarkBorder(bool d) +{ + // respect explicit opt-out and incompatible palettes or styles + d = d && shouldApplyDarkFrame(window()); + + setDarkBorderToWindow(m_data.hwnd, d); +} + QWindowsMenuBar *QWindowsWindow::menuBar() const { return m_menuBar.data(); @@ -2902,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. @@ -2914,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; @@ -2922,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); } @@ -2946,9 +3370,9 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err) return &m_vkSurface; } #elif defined(QT_NO_OPENGL) - Q_UNUSED(err) - Q_UNUSED(nativeConfig) - return 0; + Q_UNUSED(err); + Q_UNUSED(nativeConfig); + return nullptr; #endif #ifndef QT_NO_OPENGL if (!m_surface) { @@ -2957,6 +3381,8 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err) } return m_surface; +#else + return nullptr; #endif } @@ -2979,46 +3405,28 @@ void QWindowsWindow::invalidateSurface() #endif // QT_NO_OPENGL } -void QWindowsWindow::setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes) +void QWindowsWindow::registerTouchWindow() { - if (!window->handle()) + if ((QWindowsContext::instance()->systemInfo() & QWindowsContext::SI_SupportsTouch) == 0) return; - static_cast<QWindowsWindow *>(window->handle())->registerTouchWindow(touchTypes); -} -void QWindowsWindow::registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes) -{ - if ((QWindowsContext::instance()->systemInfo() & QWindowsContext::SI_SupportsTouch) - && !testFlag(TouchRegistered)) { - ULONG touchFlags = 0; - const bool ret = IsTouchWindow(m_data.hwnd, &touchFlags); - // Return if it is not a touch window or the flags are already set by a hook - // such as HCBT_CREATEWND - if (ret || touchFlags != 0) + // Initially register or re-register to change the flags + const auto touchTypes = QWindowsIntegration::instance()->touchWindowTouchType(); + if (testFlag(TouchRegistered)) { + const auto currentTouchTypes = touchWindowTouchTypes_sys(); + if (currentTouchTypes.has_value() && currentTouchTypes.value() == touchTypes) return; - if (RegisterTouchWindow(m_data.hwnd, ULONG(touchTypes))) - setFlag(TouchRegistered); - else - 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 + ULONG touchFlags = 0; + if (touchTypes.testFlag(TouchWindowTouchType::FineTouch)) + touchFlags |= TWF_FINETOUCH; + if (touchTypes.testFlag(TouchWindowTouchType::WantPalmTouch)) + touchFlags |= TWF_WANTPALM; + if (RegisterTouchWindow(m_data.hwnd, touchFlags)) + setFlag(TouchRegistered); + else + qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName())); } void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border) @@ -3034,9 +3442,14 @@ void QWindowsWindow::setHasBorderInFullScreenDefault(bool border) m_borderInFullScreenDefault = border; } +bool QWindowsWindow::hasBorderInFullScreen() const +{ + return testFlag(HasBorderInFullScreen); +} + void QWindowsWindow::setHasBorderInFullScreen(bool border) { - if (testFlag(HasBorderInFullScreen) == border) + if (hasBorderInFullScreen() == border) return; if (border) setFlag(HasBorderInFullScreen); diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 1f8800272b..024711e7f3 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -1,56 +1,23 @@ -/**************************************************************************** -** -** 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 #include <QtCore/qt_windows.h> #include <QtCore/qpointer.h> +#include "qwindowsapplication.h" #include "qwindowscursor.h" #include <qpa/qplatformwindow.h> -#include <QtPlatformHeaders/qwindowswindowfunctions.h> +#include <qpa/qplatformwindow_p.h> #if QT_CONFIG(vulkan) #include "qwindowsvulkaninstance.h" #endif +#include <optional> + QT_BEGIN_NAMESPACE class QWindowsOleDropTarget; @@ -59,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); @@ -110,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; @@ -121,10 +90,14 @@ struct QWindowsWindowData const QString &title); }; -class QWindowsBaseWindow : public QPlatformWindow +class QWindowsBaseWindow : public QPlatformWindow, + public QNativeInterface::Private::QWindowsWindow { Q_DISABLE_COPY_MOVE(QWindowsBaseWindow) public: + using TouchWindowTouchType = QNativeInterface::Private::QWindowsApplication::TouchWindowTouchType; + using TouchWindowTouchTypes = QNativeInterface::Private::QWindowsApplication::TouchWindowTouchTypes; + explicit QWindowsBaseWindow(QWindow *window) : QPlatformWindow(window) {} WId winId() const override { return WId(handle()); } @@ -134,6 +107,12 @@ public: QPoint mapFromGlobal(const QPoint &pos) const override; virtual QMargins fullFrameMargins() const { return frameMargins_sys(); } + void setHasBorderInFullScreen(bool border) override; + bool hasBorderInFullScreen() const override; + + QMargins customMargins() const override; + void setCustomMargins(const QMargins &margins) override; + using QPlatformWindow::screenForGeometry; virtual HWND handle() const = 0; @@ -153,6 +132,7 @@ protected: QRect geometry_sys() const; void setGeometry_sys(const QRect &rect) const; QMargins frameMargins_sys() const; + std::optional<TouchWindowTouchTypes> touchWindowTouchTypes_sys() const; void hide_sys(); void raise_sys(); void lower_sys(); @@ -205,7 +185,6 @@ public: WithinSetParent = 0x2, WithinSetGeometry = 0x8, OpenGLSurface = 0x10, - OpenGL_ES2 = 0x20, OpenGLDoubleBuffered = 0x40, OpenGlPixelFormatInitialized = 0x80, BlockedByModal = 0x100, @@ -222,10 +201,11 @@ public: MaximizeToFullScreen = 0x80000, Compositing = 0x100000, HasBorderInFullScreen = 0x200000, - WithinDpiChanged = 0x400000, - VulkanSurface = 0x800000, - ResizeMoveActive = 0x1000000, - DisableNonClientScaling = 0x2000000 + VulkanSurface = 0x400000, + ResizeMoveActive = 0x800000, + DisableNonClientScaling = 0x1000000, + Direct3DSurface = 0x2000000, + RestoreOverrideCursor = 0x4000000 }; QWindowsWindow(QWindow *window, const QWindowsWindowData &data); @@ -235,10 +215,12 @@ public: using QPlatformWindow::screenForGeometry; - QSurfaceFormat format() const override { return m_format; } + QSurfaceFormat format() const override; 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; @@ -277,8 +259,8 @@ public: bool setMouseGrabEnabled(bool grab) override; inline bool hasMouseCapture() const { return GetCapture() == m_data.hwnd; } - bool startSystemResize(const QPoint &pos, Qt::Corner corner) override; - bool startSystemMove(const QPoint &pos) override; + bool startSystemResize(Qt::Edges edges) override; + bool startSystemMove() override; void setFrameStrutEventsEnabled(bool enabled) override; bool frameStrutEventsEnabled() const override { return testFlag(FrameStrutEventsEnabled); } @@ -287,21 +269,27 @@ public: HWND handle() const override { return m_data.hwnd; } bool isTopLevel() const override; + static bool setDarkBorderToWindow(HWND hwnd, bool d); + void setDarkBorder(bool d); + QWindowsMenuBar *menuBar() const; void setMenuBar(QWindowsMenuBar *mb); - QMargins customMargins() const { return m_data.customMargins; } - void setCustomMargins(const QMargins &m); + 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(); @@ -311,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; @@ -335,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); } @@ -345,16 +333,20 @@ public: enum ScreenChangeMode { FromGeometryChange, FromDpiChange }; void checkForScreenChanged(ScreenChangeMode mode = FromGeometryChange); - static void setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes); - void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch); + void registerTouchWindow(); static void setHasBorderInFullScreenStatic(QWindow *window, bool border); static void setHasBorderInFullScreenDefault(bool border); - void setHasBorderInFullScreen(bool border); + void setHasBorderInFullScreen(bool border) override; + bool hasBorderInFullScreen() const override; static QString formatWindowTitle(const QString &title); static const char *embeddedNativeParentHandleProperty; static const char *hasBorderInFullScreenProperty; + 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; inline QWindowsWindowData setWindowFlags_sys(Qt::WindowFlags wt, unsigned flags = 0) const; @@ -370,6 +362,8 @@ private: void handleWindowStateChange(Qt::WindowStates state); inline void destroyIcon(); void fireExpose(const QRegion ®ion, bool force=false); + void fireFullExpose(bool force=false); + void calculateFullFrameMargins(); mutable QWindowsWindowData m_data; QPointer<QWindowsMenuBar> m_menuBar; @@ -383,10 +377,10 @@ private: QWindowsOleDropTarget *m_dropTarget = nullptr; unsigned m_savedStyle = 0; QRect m_savedFrameGeometry; - const QSurfaceFormat m_format; HICON m_iconSmall = nullptr; HICON m_iconBig = nullptr; void *m_surface = nullptr; + int m_savedDpi = 96; static bool m_screenForGLInitialized; @@ -395,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 c7c0deab3f..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,12 +15,15 @@ #include <QtGui/private/qguiapplication_p.h> #include <QtCore/qt_windows.h> #include <qpa/qplatformintegration.h> -#include <QtWindowsUIAutomationSupport/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() { @@ -70,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()) @@ -78,29 +47,83 @@ 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; } } return false; } +// Retrieve sound name by checking the icon property of a message box +// should it be the event object. +static QString alertSound(const QObject *object) +{ + if (object->inherits("QMessageBox")) { + enum MessageBoxIcon { // Keep in sync with QMessageBox::Icon + Information = 1, + Warning = 2, + Critical = 3 + }; + switch (object->property("icon").toInt()) { + case Information: + return QStringLiteral("SystemAsterisk"); + case Warning: + return QStringLiteral("SystemExclamation"); + case Critical: + return QStringLiteral("SystemHand"); + } + return QString(); + } + return QStringLiteral("SystemAsterisk"); +} + +static QString soundFileName(const QString &soundName) +{ + const QString key = "AppEvents\\Schemes\\Apps\\.Default\\"_L1 + + soundName + "\\.Current"_L1; + return QWinRegistryKey(HKEY_CURRENT_USER, key).stringValue(L""); +} + +static void playSystemSound(const QString &soundName) +{ + if (!soundName.isEmpty() && !soundFileName(soundName).isEmpty()) { + PlaySound(reinterpret_cast<const wchar_t *>(soundName.utf16()), nullptr, + SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT); + } +} + // Handles accessibility update notifications. void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) { if (!event) return; - QAccessibleInterface *accessible = event->accessibleInterface(); - if (!isActive() || !accessible || !accessible->isValid()) + // Always handle system sound events + switch (event->type()) { + case QAccessible::PopupMenuStart: + playSystemSound(QStringLiteral("MenuPopup")); + break; + case QAccessible::MenuCommand: + playSystemSound(QStringLiteral("MenuCommand")); + break; + case QAccessible::Alert: + playSystemSound(alertSound(event->object())); + break; + default: + break; + } + + // 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()) { @@ -113,6 +136,9 @@ void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event case QAccessible::ValueChanged: QWindowsUiaMainProvider::notifyValueChange(static_cast<QAccessibleValueChangeEvent *>(event)); break; + case QAccessible::NameChanged: + QWindowsUiaMainProvider::notifyNameChange(event); + break; case QAccessible::SelectionAdd: QWindowsUiaMainProvider::notifySelectionChange(event); break; 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 a20df28e3f..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 <QtWindowsUIAutomationSupport/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 new file mode 100644 index 0000000000..8eb9baa8ef --- /dev/null +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.cpp @@ -0,0 +1,90 @@ +// 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) + +#include "qwindowsuiaexpandcollapseprovider.h" +#include "qwindowsuiautils.h" +#include "qwindowscontext.h" + +#include <QtGui/qaccessible.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/qstring.h> + +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) +{ +} + +QWindowsUiaExpandCollapseProvider::~QWindowsUiaExpandCollapseProvider() = default; + +HRESULT STDMETHODCALLTYPE QWindowsUiaExpandCollapseProvider::Expand() +{ + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible) + return UIA_E_ELEMENTNOTAVAILABLE; + + QAccessibleActionInterface *actionInterface = accessible->actionInterface(); + if (!actionInterface) + return UIA_E_ELEMENTNOTAVAILABLE; + + if (!isExpanded(accessible)) + actionInterface->doAction(QAccessibleActionInterface::showMenuAction()); + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaExpandCollapseProvider::Collapse() +{ + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible) + return UIA_E_ELEMENTNOTAVAILABLE; + + QAccessibleActionInterface *actionInterface = accessible->actionInterface(); + if (!actionInterface) + return UIA_E_ELEMENTNOTAVAILABLE; + + if (isExpanded(accessible)) + actionInterface->doAction(QAccessibleActionInterface::showMenuAction()); + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsUiaExpandCollapseProvider::get_ExpandCollapseState(__RPC__out ExpandCollapseState *pRetVal) +{ + qCDebug(lcQpaUiAutomation) << __FUNCTION__; + + if (!pRetVal) + return E_INVALIDARG; + *pRetVal = ExpandCollapseState_LeafNode; + + QAccessibleInterface *accessible = accessibleInterface(); + if (!accessible) + return UIA_E_ELEMENTNOTAVAILABLE; + + *pRetVal = isExpanded(accessible) ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed; + + return S_OK; +} + +QT_END_NAMESPACE + +#endif // QT_CONFIG(accessibility) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h new file mode 100644 index 0000000000..b384eb521c --- /dev/null +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h @@ -0,0 +1,33 @@ +// 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 + +#include <QtGui/qtguiglobal.h> +#if QT_CONFIG(accessibility) + +#include "qwindowsuiabaseprovider.h" + +QT_BEGIN_NAMESPACE + +// Implements the Expand/Collapse control pattern provider. Used for menu items with submenus. +class QWindowsUiaExpandCollapseProvider : public QWindowsUiaBaseProvider, + public QComObject<IExpandCollapseProvider> +{ + Q_DISABLE_COPY_MOVE(QWindowsUiaExpandCollapseProvider) +public: + explicit QWindowsUiaExpandCollapseProvider(QAccessible::Id id); + virtual ~QWindowsUiaExpandCollapseProvider() override; + + // IExpandCollapseProvider + HRESULT STDMETHODCALLTYPE Expand() override; + HRESULT STDMETHODCALLTYPE Collapse() override; + HRESULT STDMETHODCALLTYPE get_ExpandCollapseState(__RPC__out ExpandCollapseState *pRetVal) override; +}; + +QT_END_NAMESPACE + +#endif // QT_CONFIG(accessibility) + +#endif // QWINDOWSUIAEXPANDCOLLAPSEPROVIDER_H 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 e5a9f275ae..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) @@ -53,7 +17,7 @@ #include "qwindowsuiagridprovider.h" #include "qwindowsuiagriditemprovider.h" #include "qwindowsuiawindowprovider.h" -#include "qwindowscombase.h" +#include "qwindowsuiaexpandcollapseprovider.h" #include "qwindowscontext.h" #include "qwindowsuiautils.h" #include "qwindowsuiaprovidercache.h" @@ -62,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> @@ -73,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; @@ -93,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)) { } @@ -106,9 +73,13 @@ QWindowsUiaMainProvider::~QWindowsUiaMainProvider() void QWindowsUiaMainProvider::notifyFocusChange(QAccessibleEvent *event) { if (QAccessibleInterface *accessible = event->accessibleInterface()) { - 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); } } @@ -125,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); } } } @@ -134,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); } } } @@ -163,13 +138,13 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve } } } - if (event->value().type() == QVariant::String) { + if (event->value().typeId() == QMetaType::QString) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { // Notifies changes in string values. VARIANT oldVal, newVal; clearVariant(&oldVal); setVariantString(event->value().toString(), &newVal); - QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); + UiaRaiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); } } else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) { if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { @@ -177,7 +152,24 @@ 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); + } + } + } +} + +void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *event) +{ + if (QAccessibleInterface *accessible = event->accessibleInterface()) { + // 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); } } } @@ -187,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); } } } @@ -199,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); } } } @@ -214,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) @@ -288,21 +275,25 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow } break; case UIA_TogglePatternId: - // Checkbox controls. - if (accessible->role() == QAccessible::CheckBox) { + // Checkboxes and other checkable controls. + if (accessible->state().checkable) *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; @@ -340,6 +331,16 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow *pRetVal = new QWindowsUiaInvokeProvider(id()); } break; + case UIA_ExpandCollapsePatternId: + // Menu items with submenus. + if ((accessible->role() == QAccessible::MenuItem + && accessible->childCount() > 0 + && accessible->child(0)->role() == QAccessible::PopupMenu) + || accessible->role() == QAccessible::ComboBox + || (accessible->role() == QAccessible::TreeItem && accessible->state().expandable)) { + *pRetVal = new QWindowsUiaExpandCollapseProvider(id()); + } + break; default: break; } @@ -347,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; @@ -377,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; @@ -392,12 +424,14 @@ 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. - static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE"); + // The native OSK should be disabled if the Qt OSK is in use, + // or if disabled via application attribute. + static bool imModuleEmpty = QPlatformInputContextFactory::requested().isEmpty(); + bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard); // If we want to disable the native OSK auto-showing // we have to report text fields as non-editable. - if (controlType == UIA_EditControlTypeId && !imModuleEmpty) + if (controlType == UIA_EditControlTypeId && (!imModuleEmpty || nativeVKDisabled)) controlType = UIA_TextControlTypeId; setVariantI4(controlType, pRetVal); @@ -445,6 +479,10 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR setVariantBool(wt == Qt::Popup || wt == Qt::ToolTip || wt == Qt::SplashScreen, pRetVal); } break; + case UIA_IsDialogPropertyId: + setVariantBool(accessible->role() == QAccessible::Dialog + || accessible->role() == QAccessible::AlertMessage, pRetVal); + break; case UIA_FullDescriptionPropertyId: setVariantString(accessible->text(QAccessible::Description), pRetVal); break; @@ -470,9 +508,9 @@ QString QWindowsUiaMainProvider::automationIdForAccessible(const QAccessibleInte while (obj) { QString name = obj->objectName(); if (name.isEmpty()) - return QString(); + return result; if (!result.isEmpty()) - result.prepend(QLatin1Char('.')); + result.prepend(u'.'); result.prepend(name); obj = obj->parent(); } @@ -491,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; @@ -681,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 cdc239ed99..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,25 +20,23 @@ 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); static void notifyValueChange(QAccessibleValueChangeEvent *event); + static void notifyNameChange(QAccessibleEvent *event); static void notifySelectionChange(QAccessibleEvent *event); static void notifyTextChange(QAccessibleEvent *event); // IUnknown HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override; - ULONG STDMETHODCALLTYPE AddRef() override; ULONG STDMETHODCALLTYPE Release() override; // IRawElementProviderSimple methods @@ -96,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 fb41012cf4..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) @@ -49,7 +13,6 @@ #include <QtCore/qloggingcategory.h> #include <QtCore/qstring.h> #include <QtCore/qlist.h> -#include <QtCore/qvector.h> QT_BEGIN_NAMESPACE @@ -78,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. - QVector<QAccessibleInterface *> selectedList; - for (int i = 0; i < accessible->childCount(); ++i) { - if (QAccessibleInterface *child = accessible->child(i)) { - if (child->state().selected) { - selectedList.append(child); + // First get/create list of selected items, then build a safe array with the right size. + QList<QAccessibleInterface *> selectedList; + 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); + } + } } } } @@ -127,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 d8b8f7281d..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) @@ -143,7 +107,9 @@ HRESULT QWindowsUiaTextRangeProvider::ExpandToEnclosingUnit(TextUnit unit) m_endOffset = m_startOffset + 1; } else { QString text = textInterface->text(0, len); - for (int t = m_startOffset; t >= 0; --t) { + const int start = m_startOffset >= 0 && m_startOffset < len + ? m_startOffset : len - 1; + for (int t = start; t >= 0; --t) { if (!isTextUnitSeparator(unit, text[t]) && ((t == 0) || isTextUnitSeparator(unit, text[t - 1]))) { m_startOffset = t; break; @@ -201,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; } @@ -341,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 { @@ -419,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); @@ -443,7 +418,9 @@ HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByUnit(TextPatternRangeEndpoin } m_endOffset = qBound(m_startOffset, m_endOffset, len); } else { - for (int t = m_startOffset - 1; (t >= 0) && (moved > count); --t) { + const int start = m_startOffset >= 0 && m_startOffset <= len + ? m_startOffset : len; + for (int t = start - 1; (t >= 0) && (moved > count); --t) { if (!isTextUnitSeparator(unit, text[t]) && ((t == 0) || isTextUnitSeparator(unit, text[t - 1]))) { m_startOffset = t; --moved; @@ -549,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 ab04384616..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) @@ -132,22 +96,19 @@ void setVariantString(const QString &value, VARIANT *variant) void rectToNativeUiaRect(const QRect &rect, const QWindow *w, UiaRect *uiaRect) { if (w && uiaRect) { - const qreal factor = QHighDpiScaling::factor(w); - uiaRect->left = qreal(rect.x()) * factor; - uiaRect->top = qreal(rect.y()) * factor; - uiaRect->width = qreal(rect.width()) * factor; - uiaRect->height = qreal(rect.height()) * factor; + QRectF r = QHighDpi::toNativePixels(QRectF(rect), w); + uiaRect->left =r.x(); + uiaRect->top = r.y(); + uiaRect->width = r.width(); + uiaRect->height = r.height(); } } // Scales a point from native coordinates, according to high dpi settings. void nativeUiaPointToPoint(const UiaPoint &uiaPoint, const QWindow *w, QPoint *point) { - if (w && point) { - const qreal factor = QHighDpiScaling::factor(w); - point->setX(int(std::lround(uiaPoint.x / factor))); - point->setY(int(std::lround(uiaPoint.y / factor))); - } + if (w && point) + *point = QHighDpi::fromNativePixels(QPoint(uiaPoint.x, uiaPoint.y), w); } // Maps an accessibility role ID to an UI Automation control type ID. @@ -161,7 +122,7 @@ long roleToControlTypeId(QAccessible::Role role) {QAccessible::Sound, UIA_CustomControlTypeId}, {QAccessible::Cursor, UIA_CustomControlTypeId}, {QAccessible::Caret, UIA_CustomControlTypeId}, - {QAccessible::AlertMessage, UIA_CustomControlTypeId}, + {QAccessible::AlertMessage, UIA_WindowControlTypeId}, {QAccessible::Window, UIA_WindowControlTypeId}, {QAccessible::Client, UIA_GroupControlTypeId}, {QAccessible::PopupMenu, UIA_MenuControlTypeId}, @@ -215,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 6a482f6c1c..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 <QtWindowsUIAutomationSupport/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 8651bcff60..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) @@ -72,7 +36,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaValueProvider::SetValue(LPCWSTR val) return UIA_E_ELEMENTNOTAVAILABLE; // First sets the value as a text. - QString strVal = QString::fromUtf16(reinterpret_cast<const ushort *>(val)); + QString strVal = QString::fromUtf16(reinterpret_cast<const char16_t *>(val)); accessible->setText(QAccessible::Value, strVal); // Then, if the control supports the value interface (range value) 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: diff --git a/src/plugins/platforms/windows/uiautomation/uiautomation.pri b/src/plugins/platforms/windows/uiautomation/uiautomation.pri deleted file mode 100644 index ee9332e7ea..0000000000 --- a/src/plugins/platforms/windows/uiautomation/uiautomation.pri +++ /dev/null @@ -1,44 +0,0 @@ -qtHaveModule(windowsuiautomation_support-private): \ - QT += windowsuiautomation_support-private - -SOURCES += \ - $$PWD/qwindowsuiaaccessibility.cpp \ - $$PWD/qwindowsuiaprovidercache.cpp \ - $$PWD/qwindowsuiamainprovider.cpp \ - $$PWD/qwindowsuiabaseprovider.cpp \ - $$PWD/qwindowsuiavalueprovider.cpp \ - $$PWD/qwindowsuiatextprovider.cpp \ - $$PWD/qwindowsuiatextrangeprovider.cpp \ - $$PWD/qwindowsuiatoggleprovider.cpp \ - $$PWD/qwindowsuiaselectionprovider.cpp \ - $$PWD/qwindowsuiaselectionitemprovider.cpp \ - $$PWD/qwindowsuiainvokeprovider.cpp \ - $$PWD/qwindowsuiarangevalueprovider.cpp \ - $$PWD/qwindowsuiatableprovider.cpp \ - $$PWD/qwindowsuiatableitemprovider.cpp \ - $$PWD/qwindowsuiagridprovider.cpp \ - $$PWD/qwindowsuiagriditemprovider.cpp \ - $$PWD/qwindowsuiawindowprovider.cpp \ - $$PWD/qwindowsuiautils.cpp - -HEADERS += \ - $$PWD/qwindowsuiaaccessibility.h \ - $$PWD/qwindowsuiaprovidercache.h \ - $$PWD/qwindowsuiamainprovider.h \ - $$PWD/qwindowsuiabaseprovider.h \ - $$PWD/qwindowsuiavalueprovider.h \ - $$PWD/qwindowsuiatextprovider.h \ - $$PWD/qwindowsuiatextrangeprovider.h \ - $$PWD/qwindowsuiatoggleprovider.h \ - $$PWD/qwindowsuiaselectionprovider.h \ - $$PWD/qwindowsuiaselectionitemprovider.h \ - $$PWD/qwindowsuiainvokeprovider.h \ - $$PWD/qwindowsuiarangevalueprovider.h \ - $$PWD/qwindowsuiatableprovider.h \ - $$PWD/qwindowsuiatableitemprovider.h \ - $$PWD/qwindowsuiagridprovider.h \ - $$PWD/qwindowsuiagriditemprovider.h \ - $$PWD/qwindowsuiawindowprovider.h \ - $$PWD/qwindowsuiautils.h - -mingw: QMAKE_USE *= uuid diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri deleted file mode 100644 index 95ba961df1..0000000000 --- a/src/plugins/platforms/windows/windows.pri +++ /dev/null @@ -1,130 +0,0 @@ -# Note: OpenGL32 must precede Gdi32 as it overwrites some functions. -LIBS += -lwinspool -limm32 -loleaut32 - -QT_FOR_CONFIG += gui - -qtConfig(opengl):!qtConfig(opengles2):!qtConfig(dynamicgl): LIBS *= -lopengl32 - -mingw: QMAKE_USE *= uuid -# For the dialog helpers: -LIBS += -lshlwapi -lwtsapi32 - -QMAKE_USE_PRIVATE += \ - advapi32 \ - d3d9/nolink \ - ole32 \ - shell32 \ - user32 \ - winmm - -DEFINES *= QT_NO_CAST_FROM_ASCII QT_NO_FOREACH - -SOURCES += \ - $$PWD/qwindowswindow.cpp \ - $$PWD/qwindowsintegration.cpp \ - $$PWD/qwindowscontext.cpp \ - $$PWD/qwindowsscreen.cpp \ - $$PWD/qwindowskeymapper.cpp \ - $$PWD/qwindowsmousehandler.cpp \ - $$PWD/qwindowspointerhandler.cpp \ - $$PWD/qwindowsole.cpp \ - $$PWD/qwindowsdropdataobject.cpp \ - $$PWD/qwindowsmime.cpp \ - $$PWD/qwindowsinternalmimedata.cpp \ - $$PWD/qwindowscursor.cpp \ - $$PWD/qwindowsinputcontext.cpp \ - $$PWD/qwindowstheme.cpp \ - $$PWD/qwindowsmenu.cpp \ - $$PWD/qwindowsdialoghelpers.cpp \ - $$PWD/qwindowsservices.cpp \ - $$PWD/qwindowsnativeinterface.cpp \ - $$PWD/qwindowsopengltester.cpp \ - $$PWD/qwin10helpers.cpp - -HEADERS += \ - $$PWD/qwindowscombase.h \ - $$PWD/qwindowswindow.h \ - $$PWD/qwindowsintegration.h \ - $$PWD/qwindowscontext.h \ - $$PWD/qwindowsscreen.h \ - $$PWD/qwindowskeymapper.h \ - $$PWD/qwindowsmousehandler.h \ - $$PWD/qwindowspointerhandler.h \ - $$PWD/qtwindowsglobal.h \ - $$PWD/qwindowsole.h \ - $$PWD/qwindowsdropdataobject.h \ - $$PWD/qwindowsmime.h \ - $$PWD/qwindowsinternalmimedata.h \ - $$PWD/qwindowscursor.h \ - $$PWD/qwindowsinputcontext.h \ - $$PWD/qwindowstheme.h \ - $$PWD/qwindowsmenu.h \ - $$PWD/qwindowsdialoghelpers.h \ - $$PWD/qwindowsservices.h \ - $$PWD/qwindowsnativeinterface.h \ - $$PWD/qwindowsopengltester.h \ - $$PWD/qwindowsthreadpoolrunner.h \ - $$PWD/qwin10helpers.h - -INCLUDEPATH += $$PWD - -qtConfig(opengl): HEADERS += $$PWD/qwindowsopenglcontext.h - -qtConfig(opengles2) { - SOURCES += $$PWD/qwindowseglcontext.cpp - HEADERS += $$PWD/qwindowseglcontext.h -} else: qtConfig(opengl) { - SOURCES += $$PWD/qwindowsglcontext.cpp - HEADERS += $$PWD/qwindowsglcontext.h -} - -# Dynamic GL needs both WGL and EGL -qtConfig(dynamicgl) { - SOURCES += $$PWD/qwindowseglcontext.cpp - HEADERS += $$PWD/qwindowseglcontext.h -} - -qtConfig(systemtrayicon) { - SOURCES += $$PWD/qwindowssystemtrayicon.cpp - HEADERS += $$PWD/qwindowssystemtrayicon.h -} - -qtConfig(vulkan) { - SOURCES += $$PWD/qwindowsvulkaninstance.cpp - HEADERS += $$PWD/qwindowsvulkaninstance.h -} - -qtConfig(clipboard) { - SOURCES += $$PWD/qwindowsclipboard.cpp - HEADERS += $$PWD/qwindowsclipboard.h - # drag and drop on windows only works if a clipboard is available - qtConfig(draganddrop) { - HEADERS += $$PWD/qwindowsdrag.h - SOURCES += $$PWD/qwindowsdrag.cpp - } -} - -qtConfig(tabletevent) { - INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/wintab - HEADERS += $$PWD/qwindowstabletsupport.h - SOURCES += $$PWD/qwindowstabletsupport.cpp -} - -qtConfig(sessionmanager) { - SOURCES += $$PWD/qwindowssessionmanager.cpp - HEADERS += $$PWD/qwindowssessionmanager.h -} - -qtConfig(imageformat_png):RESOURCES += $$PWD/cursors.qrc - -RESOURCES += $$PWD/openglblacklists.qrc - -qtConfig(accessibility): include($$PWD/uiautomation/uiautomation.pri) - -qtConfig(combined-angle-lib) { - DEFINES *= LIBEGL_NAME=$${LIBQTANGLE_NAME} - DEFINES *= LIBGLESV2_NAME=$${LIBQTANGLE_NAME} -} else { - DEFINES *= LIBEGL_NAME=$${LIBEGL_NAME} - DEFINES *= LIBGLESV2_NAME=$${LIBGLESV2_NAME} -} diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro deleted file mode 100644 index 50a3bb41a9..0000000000 --- a/src/plugins/platforms/windows/windows.pro +++ /dev/null @@ -1,32 +0,0 @@ -TARGET = qwindows - -QT += \ - core-private gui-private \ - eventdispatcher_support-private \ - fontdatabase_support-private theme_support-private - -qtConfig(accessibility): QT += accessibility_support-private -qtConfig(vulkan): QT += vulkan_support-private - -LIBS += -ldwmapi -QMAKE_USE_PRIVATE += gdi32 - -include(windows.pri) - -SOURCES += \ - main.cpp \ - qwindowsbackingstore.cpp \ - qwindowsgdiintegration.cpp \ - qwindowsgdinativeinterface.cpp - -HEADERS += \ - qwindowsbackingstore.h \ - qwindowsgdiintegration.h \ - qwindowsgdinativeinterface.h - -OTHER_FILES += windows.json - -PLUGIN_TYPE = platforms -PLUGIN_CLASS_NAME = QWindowsIntegrationPlugin -!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - -load(qt_plugin) |