diff options
Diffstat (limited to 'src/gui/kernel/qguiapplication.cpp')
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 732 |
1 files changed, 447 insertions, 285 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 313f1bcb54..c97374e975 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module 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) 2021 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qguiapplication.h" @@ -45,6 +9,7 @@ #include <qpa/qplatformintegrationfactory_p.h> #include "private/qevent_p.h" #include "private/qeventpoint_p.h" +#include "private/qiconloader_p.h" #include "qfont.h" #include "qpointingdevice.h" #include <qpa/qplatformfontdatabase.h> @@ -52,21 +17,24 @@ #include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformtheme.h> #include <qpa/qplatformintegration.h> +#include <qpa/qplatformkeymapper.h> #include <QtCore/QAbstractEventDispatcher> +#include <QtCore/QFileInfo> #include <QtCore/QStandardPaths> #include <QtCore/QVariant> #include <QtCore/private/qcoreapplication_p.h> #include <QtCore/private/qabstracteventdispatcher_p.h> +#include <QtCore/private/qminimalflatset_p.h> #include <QtCore/qmutex.h> #include <QtCore/private/qthread_p.h> #include <QtCore/private/qlocking_p.h> #include <QtCore/private/qflatmap_p.h> #include <QtCore/qdir.h> #include <QtCore/qlibraryinfo.h> -#include <QtCore/qnumeric.h> +#include <QtCore/private/qnumeric_p.h> #include <QtDebug> -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) #include "qaccessible.h" #endif #include <qpalette.h> @@ -77,6 +45,7 @@ #include <QtGui/qgenericpluginfactory.h> #include <QtGui/qstylehints.h> +#include <QtGui/private/qstylehints_p.h> #include <QtGui/qinputmethod.h> #include <QtGui/qpixmapcache.h> #include <qpa/qplatforminputcontext.h> @@ -85,8 +54,11 @@ #include <qpa/qwindowsysteminterface.h> #include <qpa/qwindowsysteminterface_p.h> #include "private/qwindow_p.h" +#include "private/qicon_p.h" #include "private/qcursor_p.h" -#include "private/qopenglcontext_p.h" +#if QT_CONFIG(opengl) +# include "private/qopenglcontext_p.h" +#endif #include "private/qinputdevicemanager_p.h" #include "private/qinputmethod_p.h" #include "private/qpointingdevice_p.h" @@ -123,12 +95,21 @@ #include <emscripten.h> #endif +#if QT_CONFIG(vulkan) +#include <private/qvulkandefaultinstance_p.h> +#endif + #include <qtgui_tracepoints_p.h> -#include <ctype.h> +#include <private/qtools_p.h> + +#include <limits> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; +using namespace QtMiscUtils; + // Helper macro for static functions to check on the existence of the application class. #define CHECK_QAPP_INSTANCE(...) \ if (Q_LIKELY(QCoreApplication::instance())) { \ @@ -138,80 +119,81 @@ QT_BEGIN_NAMESPACE } Q_CORE_EXPORT void qt_call_post_routines(); -Q_GUI_EXPORT bool qt_is_gui_used = true; +Q_CONSTINIT Q_GUI_EXPORT bool qt_is_tty_app = false; -Qt::MouseButtons QGuiApplicationPrivate::mouse_buttons = Qt::NoButton; -Qt::KeyboardModifiers QGuiApplicationPrivate::modifier_buttons = Qt::NoModifier; +Q_CONSTINIT Qt::MouseButtons QGuiApplicationPrivate::mouse_buttons = Qt::NoButton; +Q_CONSTINIT Qt::KeyboardModifiers QGuiApplicationPrivate::modifier_buttons = Qt::NoModifier; -QPointF QGuiApplicationPrivate::lastCursorPosition(qInf(), qInf()); +Q_CONSTINIT QGuiApplicationPrivate::QLastCursorPosition QGuiApplicationPrivate::lastCursorPosition; -QWindow *QGuiApplicationPrivate::currentMouseWindow = nullptr; +Q_CONSTINIT QWindow *QGuiApplicationPrivate::currentMouseWindow = nullptr; -QString QGuiApplicationPrivate::styleOverride; +Q_CONSTINIT QString QGuiApplicationPrivate::styleOverride; -Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive; +Q_CONSTINIT Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive; -Qt::HighDpiScaleFactorRoundingPolicy QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = +Q_CONSTINIT Qt::HighDpiScaleFactorRoundingPolicy QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = Qt::HighDpiScaleFactorRoundingPolicy::PassThrough; -QPointer<QWindow> QGuiApplicationPrivate::currentDragWindow; +Q_CONSTINIT QPointer<QWindow> QGuiApplicationPrivate::currentDragWindow; -QList<QGuiApplicationPrivate::TabletPointData> QGuiApplicationPrivate::tabletDevicePoints; // TODO remove +Q_CONSTINIT QList<QGuiApplicationPrivate::TabletPointData> QGuiApplicationPrivate::tabletDevicePoints; // TODO remove -QPlatformIntegration *QGuiApplicationPrivate::platform_integration = nullptr; -QPlatformTheme *QGuiApplicationPrivate::platform_theme = nullptr; +Q_CONSTINIT QPlatformIntegration *QGuiApplicationPrivate::platform_integration = nullptr; +Q_CONSTINIT QPlatformTheme *QGuiApplicationPrivate::platform_theme = nullptr; -QList<QObject *> QGuiApplicationPrivate::generic_plugin_list; +Q_CONSTINIT QList<QObject *> QGuiApplicationPrivate::generic_plugin_list; enum ApplicationResourceFlags { ApplicationFontExplicitlySet = 0x2 }; -static unsigned applicationResourceFlags = 0; +Q_CONSTINIT static unsigned applicationResourceFlags = 0; -QIcon *QGuiApplicationPrivate::app_icon = nullptr; +Q_CONSTINIT QIcon *QGuiApplicationPrivate::app_icon = nullptr; -QString *QGuiApplicationPrivate::platform_name = nullptr; -QString *QGuiApplicationPrivate::displayName = nullptr; -QString *QGuiApplicationPrivate::desktopFileName = nullptr; +Q_CONSTINIT QString *QGuiApplicationPrivate::platform_name = nullptr; +Q_CONSTINIT QString *QGuiApplicationPrivate::displayName = nullptr; +Q_CONSTINIT QString *QGuiApplicationPrivate::desktopFileName = nullptr; -QPalette *QGuiApplicationPrivate::app_pal = nullptr; // default application palette +Q_CONSTINIT QPalette *QGuiApplicationPrivate::app_pal = nullptr; // default application palette -Qt::MouseButton QGuiApplicationPrivate::mousePressButton = Qt::NoButton; +Q_CONSTINIT Qt::MouseButton QGuiApplicationPrivate::mousePressButton = Qt::NoButton; -static int mouseDoubleClickDistance = 0; -static int touchDoubleTapDistance = 0; +Q_CONSTINIT static int mouseDoubleClickDistance = 0; +Q_CONSTINIT static int touchDoubleTapDistance = 0; -QWindow *QGuiApplicationPrivate::currentMousePressWindow = nullptr; +Q_CONSTINIT QWindow *QGuiApplicationPrivate::currentMousePressWindow = nullptr; -static Qt::LayoutDirection layout_direction = Qt::LayoutDirectionAuto; -static bool force_reverse = false; +Q_CONSTINIT static Qt::LayoutDirection layout_direction = Qt::LayoutDirectionAuto; +Q_CONSTINIT static Qt::LayoutDirection effective_layout_direction = Qt::LeftToRight; +Q_CONSTINIT static bool force_reverse = false; -QGuiApplicationPrivate *QGuiApplicationPrivate::self = nullptr; -int QGuiApplicationPrivate::m_fakeMouseSourcePointId = -1; +Q_CONSTINIT QGuiApplicationPrivate *QGuiApplicationPrivate::self = nullptr; +Q_CONSTINIT int QGuiApplicationPrivate::m_fakeMouseSourcePointId = -1; #ifndef QT_NO_CLIPBOARD -QClipboard *QGuiApplicationPrivate::qt_clipboard = nullptr; +Q_CONSTINIT QClipboard *QGuiApplicationPrivate::qt_clipboard = nullptr; #endif -QList<QScreen *> QGuiApplicationPrivate::screen_list; +Q_CONSTINIT QList<QScreen *> QGuiApplicationPrivate::screen_list; -QWindowList QGuiApplicationPrivate::window_list; -QWindow *QGuiApplicationPrivate::focus_window = nullptr; +Q_CONSTINIT QWindowList QGuiApplicationPrivate::window_list; +Q_CONSTINIT QWindow *QGuiApplicationPrivate::focus_window = nullptr; -static QBasicMutex applicationFontMutex; -QFont *QGuiApplicationPrivate::app_font = nullptr; -QStyleHints *QGuiApplicationPrivate::styleHints = nullptr; -bool QGuiApplicationPrivate::obey_desktop_settings = true; +Q_CONSTINIT static QBasicMutex applicationFontMutex; +Q_CONSTINIT QFont *QGuiApplicationPrivate::app_font = nullptr; +Q_CONSTINIT QStyleHints *QGuiApplicationPrivate::styleHints = nullptr; +Q_CONSTINIT bool QGuiApplicationPrivate::obey_desktop_settings = true; -QInputDeviceManager *QGuiApplicationPrivate::m_inputDeviceManager = nullptr; +Q_CONSTINIT QInputDeviceManager *QGuiApplicationPrivate::m_inputDeviceManager = nullptr; -qreal QGuiApplicationPrivate::m_maxDevicePixelRatio = 0.0; +Q_CONSTINIT qreal QGuiApplicationPrivate::m_maxDevicePixelRatio = 0.0; -static qreal fontSmoothingGamma = 1.7; +Q_CONSTINIT static qreal fontSmoothingGamma = 1.7; -bool QGuiApplicationPrivate::quitOnLastWindowClosed = true; +Q_CONSTINIT bool QGuiApplicationPrivate::quitOnLastWindowClosed = true; extern void qRegisterGuiVariant(); #if QT_CONFIG(animation) @@ -224,7 +206,7 @@ static bool qt_detectRTLLanguage() (QGuiApplication::tr("QT_LAYOUT_DIRECTION", "Translate this string to the string 'LTR' in left-to-right" " languages or to 'RTL' in right-to-left languages (such as Hebrew" - " and Arabic) to get proper widget layout.") == QLatin1String("RTL")); + " and Arabic) to get proper widget layout.") == "RTL"_L1); } static void initFontUnlocked() @@ -254,7 +236,7 @@ static void initThemeHints() static bool checkNeedPortalSupport() { #if QT_CONFIG(dbus) - return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty() || qEnvironmentVariableIsSet("SNAP"); + return QFileInfo::exists("/.flatpak-info"_L1) || qEnvironmentVariableIsSet("SNAP"); #else return false; #endif // QT_CONFIG(dbus) @@ -281,20 +263,20 @@ struct QWindowGeometrySpecification static inline int nextGeometryToken(const QByteArray &a, int &pos, char *op) { *op = 0; - const int size = a.size(); + const qsizetype size = a.size(); if (pos >= size) return -1; *op = a.at(pos); if (*op == '+' || *op == '-' || *op == 'x') pos++; - else if (isdigit(*op)) + else if (isAsciiDigit(*op)) *op = 'x'; // If it starts with a digit, it is supposed to be a width specification. else return -1; const int numberPos = pos; - for ( ; pos < size && isdigit(a.at(pos)); ++pos) ; + for ( ; pos < size && isAsciiDigit(a.at(pos)); ++pos) ; bool ok; const int result = a.mid(numberPos, pos - numberPos).toInt(&ok); @@ -582,10 +564,12 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME \list \li \c {altgr}, detect the key \c {AltGr} found on some keyboards as Qt::GroupSwitchModifier (since Qt 5.12). - \li \c {darkmode=[1|2]} controls how Qt responds to the activation + \li \c {darkmode=[0|1|2]} controls how Qt responds to the activation of the \e{Dark Mode for applications} introduced in Windows 10 1903 (since Qt 5.15). + A value of 0 disables dark mode support. + A value of 1 causes Qt to switch the window borders to black when \e{Dark Mode for applications} is activated and no High Contrast Theme is in use. This is intended for applications @@ -597,17 +581,17 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME experimental pending the introduction of new style that properly adapts to dark mode. + As of Qt 6.5, the default value is 2; to disable dark mode + support, set the value to 0 or 1. + \li \c {dialogs=[xp|none]}, \c xp uses XP-style native dialogs and \c none disables them. \li \c {fontengine=freetype}, uses the FreeType font engine. - \li \c {fontengine=directwrite}, uses the experimental DirectWrite - font database and defaults to using the DirectWrite font + \li \c {fontengine=gdi}, uses the legacy GDI-based + font database and defaults to using the GDI font engine (which is otherwise only used for some font types - or font properties.) This affects font selection and aims - to provide font naming more consistent with other platforms, - but does not support all font formats, such as Postscript - Type-1 or Microsoft FNT fonts. + or font properties.) (Since Qt 6.8). \li \c {menus=[native|none]}, controls the use of native menus. Native menus are implemented using Win32 API and are simpler than @@ -621,7 +605,8 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME \li \c {nocolorfonts} Turn off DirectWrite Color fonts (since Qt 5.8). - \li \c {nodirectwrite} Turn off DirectWrite fonts (since Qt 5.8). + \li \c {nodirectwrite} Turn off DirectWrite fonts (since Qt 5.8). This implicitly + also selects the GDI font engine. \li \c {nomousefromtouch} Ignores mouse events synthesized from touch events by the operating system. @@ -649,9 +634,9 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME #ifdef Q_QDOC QGuiApplication::QGuiApplication(int &argc, char **argv) #else -QGuiApplication::QGuiApplication(int &argc, char **argv, int flags) +QGuiApplication::QGuiApplication(int &argc, char **argv, int) #endif - : QCoreApplication(*new QGuiApplicationPrivate(argc, argv, flags)) + : QCoreApplication(*new QGuiApplicationPrivate(argc, argv)) { d_func()->init(); @@ -707,15 +692,16 @@ QGuiApplication::~QGuiApplication() QGuiApplicationPrivate::desktopFileName = nullptr; QGuiApplicationPrivate::mouse_buttons = Qt::NoButton; QGuiApplicationPrivate::modifier_buttons = Qt::NoModifier; - QGuiApplicationPrivate::lastCursorPosition = {qreal(qInf()), qreal(qInf())}; + QGuiApplicationPrivate::lastCursorPosition.reset(); QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr; QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive; + QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = Qt::HighDpiScaleFactorRoundingPolicy::PassThrough; QGuiApplicationPrivate::currentDragWindow = nullptr; QGuiApplicationPrivate::tabletDevicePoints.clear(); } -QGuiApplicationPrivate::QGuiApplicationPrivate(int &argc, char **argv, int flags) - : QCoreApplicationPrivate(argc, argv, flags), +QGuiApplicationPrivate::QGuiApplicationPrivate(int &argc, char **argv) + : QCoreApplicationPrivate(argc, argv), inputMethod(nullptr), lastTouchType(QEvent::TouchEnd), ownGlobalShareContext(false) @@ -764,13 +750,37 @@ QString QGuiApplication::applicationDisplayName() } /*! + Sets the application's badge to \a number. + + Useful for providing feedback to the user about the number + of unread messages or similar. + + The badge will be overlaid on the application's icon in the Dock + on \macos, the home screen icon on iOS, or the task bar on Windows + and Linux. + + If the number is outside the range supported by the platform, the + number will be clamped to the supported range. If the number does + not fit within the badge, the number may be visually elided. + + Setting the number to 0 will clear the badge. + + \since 6.5 + \sa applicationName +*/ +void QGuiApplication::setBadgeNumber(qint64 number) +{ + QGuiApplicationPrivate::platformIntegration()->setApplicationBadge(number); +} + +/*! \property QGuiApplication::desktopFileName \brief the base name of the desktop entry for this application \since 5.7 - This is the file name, without the full path, of the desktop entry - that represents this application according to the freedesktop desktop - entry specification. + This is the file name, without the full path or the trailing ".desktop" + extension of the desktop entry that represents this application + according to the freedesktop desktop entry specification. This property gives a precise indication of what desktop entry represents the application and it is needed by the windowing system to retrieve @@ -784,6 +794,15 @@ void QGuiApplication::setDesktopFileName(const QString &name) if (!QGuiApplicationPrivate::desktopFileName) QGuiApplicationPrivate::desktopFileName = new QString; *QGuiApplicationPrivate::desktopFileName = name; + if (name.endsWith(QLatin1String(".desktop"))) { // ### Qt 7: remove + const QString filePath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, name); + if (!filePath.isEmpty()) { + qWarning("QGuiApplication::setDesktopFileName: the specified desktop file name " + "ends with .desktop. For compatibility reasons, the .desktop suffix will " + "be removed. Please specify a desktop file name without .desktop suffix"); + (*QGuiApplicationPrivate::desktopFileName).chop(8); + } + } } QString QGuiApplication::desktopFileName() @@ -810,7 +829,7 @@ QWindow *QGuiApplication::modalWindow() CHECK_QAPP_INSTANCE(nullptr) if (QGuiApplicationPrivate::self->modalWindowList.isEmpty()) return nullptr; - return QGuiApplicationPrivate::self->modalWindowList.first(); + return QGuiApplicationPrivate::self->modalWindowList.constFirst(); } static void updateBlockedStatusRecursion(QWindow *window, bool shouldBeBlocked) @@ -861,7 +880,7 @@ void QGuiApplicationPrivate::showModalWindow(QWindow *modal) } } - for (QWindow *window : qAsConst(QGuiApplicationPrivate::window_list)) { + for (QWindow *window : std::as_const(QGuiApplicationPrivate::window_list)) { if (needsWindowBlockedEvent(window) && !window->d_func()->blockedByModalWindow) updateBlockedStatus(window); } @@ -873,12 +892,23 @@ void QGuiApplicationPrivate::hideModalWindow(QWindow *window) { self->modalWindowList.removeAll(window); - for (QWindow *window : qAsConst(QGuiApplicationPrivate::window_list)) { + for (QWindow *window : std::as_const(QGuiApplicationPrivate::window_list)) { if (needsWindowBlockedEvent(window) && window->d_func()->blockedByModalWindow) updateBlockedStatus(window); } } +Qt::WindowModality QGuiApplicationPrivate::defaultModality() const +{ + return Qt::NonModal; +} + +bool QGuiApplicationPrivate::windowNeverBlocked(QWindow *window) const +{ + Q_UNUSED(window); + return false; +} + /* Returns \c true if \a window is blocked by a modal window. If \a blockingWindow is non-zero, *blockingWindow will be set to the blocking @@ -886,55 +916,40 @@ void QGuiApplicationPrivate::hideModalWindow(QWindow *window) */ bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blockingWindow) const { + Q_ASSERT_X(window, Q_FUNC_INFO, "The window must not be null"); + QWindow *unused = nullptr; if (!blockingWindow) blockingWindow = &unused; + *blockingWindow = nullptr; - if (modalWindowList.isEmpty()) { - *blockingWindow = nullptr; + if (modalWindowList.isEmpty() || windowNeverBlocked(window)) return false; - } - for (int i = 0; i < modalWindowList.count(); ++i) { + for (int i = 0; i < modalWindowList.size(); ++i) { QWindow *modalWindow = modalWindowList.at(i); // A window is not blocked by another modal window if the two are // the same, or if the window is a child of the modal window. - if (window == modalWindow || modalWindow->isAncestorOf(window, QWindow::IncludeTransients)) { - *blockingWindow = nullptr; + if (window == modalWindow || modalWindow->isAncestorOf(window, QWindow::IncludeTransients)) return false; - } - Qt::WindowModality windowModality = modalWindow->modality(); - switch (windowModality) { + switch (modalWindow->modality() == Qt::NonModal ? defaultModality() + : modalWindow->modality()) { case Qt::ApplicationModal: - { - if (modalWindow != window) { - *blockingWindow = modalWindow; - return true; - } - break; - } - case Qt::WindowModal: - { - QWindow *w = window; + *blockingWindow = modalWindow; + return true; + case Qt::WindowModal: { + // Find the nearest ancestor of window which is also an ancestor of modal window to + // determine if the modal window blocks the window. + auto *current = window; do { - QWindow *m = modalWindow; - do { - if (m == w) { - *blockingWindow = m; - return true; - } - QWindow *p = m->parent(); - if (!p) - p = m->transientParent(); - m = p; - } while (m); - QWindow *p = w->parent(); - if (!p) - p = w->transientParent(); - w = p; - } while (w); + if (current->isAncestorOf(modalWindow, QWindow::IncludeTransients)) { + *blockingWindow = modalWindow; + return true; + } + current = current->parent(QWindow::IncludeTransients); + } while (current); break; } default: @@ -942,13 +957,14 @@ bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blocking break; } } - *blockingWindow = nullptr; return false; } /*! Returns the QWindow that receives events tied to focus, such as key events. + + \sa QWindow::requestActivate() */ QWindow *QGuiApplication::focusWindow() { @@ -1126,7 +1142,7 @@ qreal QGuiApplication::devicePixelRatio() const return QGuiApplicationPrivate::m_maxDevicePixelRatio; QGuiApplicationPrivate::m_maxDevicePixelRatio = 1.0; // make sure we never return 0. - for (QScreen *screen : qAsConst(QGuiApplicationPrivate::screen_list)) + for (QScreen *screen : std::as_const(QGuiApplicationPrivate::screen_list)) QGuiApplicationPrivate::m_maxDevicePixelRatio = qMax(QGuiApplicationPrivate::m_maxDevicePixelRatio, screen->devicePixelRatio()); return QGuiApplicationPrivate::m_maxDevicePixelRatio; @@ -1173,32 +1189,50 @@ QWindow *QGuiApplication::topLevelAt(const QPoint &pos) \li \c offscreen \li \c qnx \li \c windows - \li \c wayland is a platform plugin for modern Linux desktops and some - embedded systems. - \li \c xcb is the X11 plugin used on regular desktop Linux platforms. + \li \c wayland is a platform plugin for the Wayland display server protocol, + used on some Linux desktops and embedded systems. + \li \c xcb is a plugin for the X11 window system, used on some desktop Linux platforms. \endlist + \note Calling this function without a QGuiApplication will return the default + platform name, if available. The default platform name is not affected by the + \c{-platform} command line option, or the \c QT_QPA_PLATFORM environment variable. + For more information about the platform plugins for embedded Linux devices, see \l{Qt for Embedded Linux}. */ QString QGuiApplication::platformName() { - return QGuiApplicationPrivate::platform_name ? - *QGuiApplicationPrivate::platform_name : QString(); + if (!QGuiApplication::instance()) { +#ifdef QT_QPA_DEFAULT_PLATFORM_NAME + return QStringLiteral(QT_QPA_DEFAULT_PLATFORM_NAME); +#else + return QString(); +#endif + } else { + return QGuiApplicationPrivate::platform_name ? + *QGuiApplicationPrivate::platform_name : QString(); + } } Q_LOGGING_CATEGORY(lcQpaPluginLoading, "qt.qpa.plugin"); +Q_LOGGING_CATEGORY(lcQpaTheme, "qt.qpa.theme"); Q_LOGGING_CATEGORY(lcPtrDispatch, "qt.pointer.dispatch"); static void init_platform(const QString &pluginNamesWithArguments, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv) { - QStringList plugins = pluginNamesWithArguments.split(QLatin1Char(';'), Qt::SkipEmptyParts); + qCDebug(lcQpaPluginLoading) << "init_platform called with" + << "pluginNamesWithArguments" << pluginNamesWithArguments + << "platformPluginPath" << platformPluginPath + << "platformThemeName" << platformThemeName; + + QStringList plugins = pluginNamesWithArguments.split(u';', Qt::SkipEmptyParts); QStringList platformArguments; QStringList availablePlugins = QPlatformIntegrationFactory::keys(platformPluginPath); for (const auto &pluginArgument : plugins) { // Split into platform name and arguments - QStringList arguments = pluginArgument.split(QLatin1Char(':'), Qt::SkipEmptyParts); + QStringList arguments = pluginArgument.split(u':', Qt::SkipEmptyParts); if (arguments.isEmpty()) continue; const QString name = arguments.takeFirst().toLower(); @@ -1208,10 +1242,16 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString argumentsKey[0] = argumentsKey.at(0).toUpper(); arguments.append(QLibraryInfo::platformPluginArguments(argumentsKey)); + qCDebug(lcQpaPluginLoading) << "Attempting to load Qt platform plugin" << name << "with arguments" << arguments; + // Create the platform integration. QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); if (Q_UNLIKELY(!QGuiApplicationPrivate::platform_integration)) { if (availablePlugins.contains(name)) { + if (name == QStringLiteral("xcb") && QVersionNumber::compare(QLibraryInfo::version(), QVersionNumber(6, 5, 0)) >= 0) { + qCWarning(lcQpaPluginLoading).nospace().noquote() + << "From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin."; + } qCInfo(lcQpaPluginLoading).nospace().noquote() << "Could not load the Qt platform plugin \"" << name << "\" in \"" << QDir::toNativeSeparators(platformPluginPath) << "\" even though it was found."; @@ -1221,6 +1261,7 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString << QDir::toNativeSeparators(platformPluginPath) << "\""; } } else { + qCDebug(lcQpaPluginLoading) << "Successfully loaded Qt platform plugin" << name; QGuiApplicationPrivate::platform_name = new QString(name); platformArguments = arguments; break; @@ -1232,7 +1273,7 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString "Reinstalling the application may fix this problem.\n"); if (!availablePlugins.isEmpty()) - fatalMessage += QStringLiteral("\nAvailable platform plugins are: %1.\n").arg(availablePlugins.join(QLatin1String(", "))); + fatalMessage += "\nAvailable platform plugins are: %1.\n"_L1.arg(availablePlugins.join(", "_L1)); #if defined(Q_OS_WIN) // Windows: Display message box unless it is a console application @@ -1249,45 +1290,57 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString // 1) Fetch the platform name from the environment if present. QStringList themeNames; - if (!platformThemeName.isEmpty()) + if (!platformThemeName.isEmpty()) { + qCDebug(lcQpaTheme) << "Adding" << platformThemeName << "from environment to list of theme names"; themeNames.append(platformThemeName); + } // 2) Special case - check whether it's a flatpak or snap app to use xdg-desktop-portal platform theme for portals support if (checkNeedPortalSupport()) { + qCDebug(lcQpaTheme) << "Adding xdgdesktopportal to list of theme names"; themeNames.append(QStringLiteral("xdgdesktopportal")); } // 3) Ask the platform integration for a list of theme names - themeNames += QGuiApplicationPrivate::platform_integration->themeNames(); + const auto platformIntegrationThemeNames = QGuiApplicationPrivate::platform_integration->themeNames(); + qCDebug(lcQpaTheme) << "Adding platform integration's theme names to list of theme names:" << platformIntegrationThemeNames; + themeNames += platformIntegrationThemeNames; // 4) Look for a theme plugin. - for (const QString &themeName : qAsConst(themeNames)) { + for (const QString &themeName : std::as_const(themeNames)) { + qCDebug(lcQpaTheme) << "Attempting to create platform theme" << themeName << "via QPlatformThemeFactory::create"; QGuiApplicationPrivate::platform_theme = QPlatformThemeFactory::create(themeName, platformPluginPath); - if (QGuiApplicationPrivate::platform_theme) + if (QGuiApplicationPrivate::platform_theme) { + qCDebug(lcQpaTheme) << "Successfully created platform theme" << themeName; break; + } } // 5) If no theme plugin was found ask the platform integration to // create a theme if (!QGuiApplicationPrivate::platform_theme) { - for (const QString &themeName : qAsConst(themeNames)) { + for (const QString &themeName : std::as_const(themeNames)) { + qCDebug(lcQpaTheme) << "Attempting to create platform theme" << themeName << "via createPlatformTheme"; QGuiApplicationPrivate::platform_theme = QGuiApplicationPrivate::platform_integration->createPlatformTheme(themeName); - if (QGuiApplicationPrivate::platform_theme) + if (QGuiApplicationPrivate::platform_theme) { + qCDebug(lcQpaTheme) << "Successfully created platform theme" << themeName; break; + } } // No error message; not having a theme plugin is allowed. } // 6) Fall back on the built-in "null" platform theme. - if (!QGuiApplicationPrivate::platform_theme) + if (!QGuiApplicationPrivate::platform_theme) { + qCDebug(lcQpaTheme) << "Failed to create platform theme; using \"null\" platform theme"; QGuiApplicationPrivate::platform_theme = new QPlatformTheme; + } -#ifndef QT_NO_PROPERTIES // Set arguments as dynamic properties on the native interface as // boolean 'foo' or strings: 'foo=bar' if (!platformArguments.isEmpty()) { if (QObject *nativeInterface = QGuiApplicationPrivate::platform_integration->nativeInterface()) { - for (const QString &argument : qAsConst(platformArguments)) { - const int equalsPos = argument.indexOf(QLatin1Char('=')); + for (const QString &argument : std::as_const(platformArguments)) { + const qsizetype equalsPos = argument.indexOf(u'='); const QByteArray name = equalsPos != -1 ? argument.left(equalsPos).toUtf8() : argument.toUtf8(); const QVariant value = @@ -1296,25 +1349,24 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString } } } -#endif const auto platformIntegration = QGuiApplicationPrivate::platformIntegration(); fontSmoothingGamma = platformIntegration->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal(); QCoreApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus, - !platformIntegration->styleHint(QPlatformIntegration::ShowShortcutsInContextMenus).toBool()); + !QGuiApplication::styleHints()->showShortcutsInContextMenus()); } static void init_plugins(const QList<QByteArray> &pluginList) { - for (int i = 0; i < pluginList.count(); ++i) { + for (int i = 0; i < pluginList.size(); ++i) { QByteArray pluginSpec = pluginList.at(i); - int colonPos = pluginSpec.indexOf(':'); + qsizetype colonPos = pluginSpec.indexOf(':'); QObject *plugin; if (colonPos < 0) - plugin = QGenericPluginFactory::create(QLatin1String(pluginSpec), QString()); + plugin = QGenericPluginFactory::create(QLatin1StringView(pluginSpec), QString()); else - plugin = QGenericPluginFactory::create(QLatin1String(pluginSpec.mid(0, colonPos)), - QLatin1String(pluginSpec.mid(colonPos+1))); + plugin = QGenericPluginFactory::create(QLatin1StringView(pluginSpec.mid(0, colonPos)), + QLatin1StringView(pluginSpec.mid(colonPos+1))); if (plugin) QGuiApplicationPrivate::generic_plugin_list.append(plugin); else @@ -1477,7 +1529,9 @@ void QGuiApplicationPrivate::createPlatformIntegration() Q_UNUSED(platformExplicitlySelected); - init_platform(QLatin1String(platformName), platformPluginPath, platformThemeName, argc, argv); + init_platform(QLatin1StringView(platformName), platformPluginPath, platformThemeName, argc, argv); + if (const QPlatformTheme *theme = platformTheme()) + QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(theme->colorScheme()); if (!icon.isEmpty()) forcedWindowIcon = QDir::isAbsolutePath(icon) ? QIcon(icon) : QIcon::fromTheme(icon); @@ -1496,7 +1550,11 @@ void QGuiApplicationPrivate::createEventDispatcher() if (platform_integration == nullptr) createPlatformIntegration(); - // The platform integration should not mess with the event dispatcher + // The platform integration should not result in creating an event dispatcher + Q_ASSERT_X(!threadData.loadRelaxed()->eventDispatcher, "QGuiApplication", + "Creating the platform integration resulted in creating an event dispatcher"); + + // Nor should it mess with the QCoreApplication's event dispatcher Q_ASSERT(!eventDispatcher); eventDispatcher = platform_integration->createEventDispatcher(); @@ -1510,7 +1568,7 @@ void QGuiApplicationPrivate::eventDispatcherReady() platform_integration->initialize(); } -void QGuiApplicationPrivate::init() +void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::init() { Q_TRACE_SCOPE(QGuiApplicationPrivate_init); @@ -1560,12 +1618,12 @@ void QGuiApplicationPrivate::init() } else if (strncmp(arg, "-psn_", 5) == 0) { // eat "-psn_xxxx" on Mac, which is passed when starting an app from Finder. // special hack to change working directory (for an app bundle) when running from finder - if (QDir::currentPath() == QLatin1String("/")) { + if (QDir::currentPath() == "/"_L1) { QCFType<CFURLRef> bundleURL(CFBundleCopyBundleURL(CFBundleGetMainBundle())); QString qbundlePath = QCFString(CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle)); - if (qbundlePath.endsWith(QLatin1String(".app"))) - QDir::setCurrent(qbundlePath.section(QLatin1Char('/'), 0, -2)); + if (qbundlePath.endsWith(".app"_L1)) + QDir::setCurrent(qbundlePath.section(u'/', 0, -2)); } #endif #ifndef QT_NO_SESSIONMANAGER @@ -1573,7 +1631,7 @@ void QGuiApplicationPrivate::init() ++i; if (argv[i] && *argv[i]) { session_id = QString::fromLatin1(argv[i]); - int p = session_id.indexOf(QLatin1Char('_')); + qsizetype p = session_id.indexOf(u'_'); if (p >= 0) { session_key = session_id.mid(p +1); session_id = session_id.left(p); @@ -1669,8 +1727,8 @@ void QGuiApplicationPrivate::init() Q_UNUSED(loadTestability); #endif // QT_CONFIG(library) - if (layout_direction == Qt::LayoutDirectionAuto || force_reverse) - QGuiApplication::setLayoutDirection(qt_detectRTLLanguage() ? Qt::RightToLeft : Qt::LeftToRight); + // trigger changed signal and event delivery + QGuiApplication::setLayoutDirection(layout_direction); if (!QGuiApplicationPrivate::displayName) QObject::connect(q, &QGuiApplication::applicationNameChanged, @@ -1684,7 +1742,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() is_app_closing = true; is_app_running = false; - for (int i = 0; i < generic_plugin_list.count(); ++i) + for (int i = 0; i < generic_plugin_list.size(); ++i) delete generic_plugin_list.at(i); generic_plugin_list.clear(); @@ -1696,7 +1754,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() QCursorData::cleanup(); #endif - layout_direction = Qt::LeftToRight; + layout_direction = Qt::LayoutDirectionAuto; cleanupThreadData(); @@ -1715,6 +1773,10 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() } #endif +#if QT_CONFIG(vulkan) + QVulkanDefaultInstance::cleanup(); +#endif + platform_integration->destroy(); delete platform_theme; @@ -1785,7 +1847,7 @@ Qt::KeyboardModifiers QGuiApplication::queryKeyboardModifiers() { CHECK_QAPP_INSTANCE(Qt::KeyboardModifiers{}) QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration(); - return pi->queryKeyboardModifiers(); + return pi->keyMapper()->queryKeyboardModifiers(); } /*! @@ -1843,9 +1905,10 @@ QFunctionPointer QGuiApplication::platformFunction(const QByteArray &function) Generally, no user interaction can take place before calling exec(). - To make your application perform idle processing, e.g., executing a special - function whenever there are no pending events, use a QTimer with 0 timeout. - More advanced idle processing schemes can be achieved using processEvents(). + To make your application perform idle processing, e.g., executing a + special function whenever there are no pending events, use a QChronoTimer + with 0ns timeout. More advanced idle processing schemes can be achieved + using processEvents(). We recommend that you connect clean-up code to the \l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in your @@ -1857,7 +1920,7 @@ QFunctionPointer QGuiApplication::platformFunction(const QByteArray &function) */ int QGuiApplication::exec() { -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QAccessible::setRootObject(qApp); #endif return QCoreApplication::exec(); @@ -1935,19 +1998,24 @@ bool QGuiApplication::notify(QObject *object, QEvent *event) */ bool QGuiApplication::event(QEvent *e) { - if (e->type() == QEvent::LanguageChange) { - setLayoutDirection(qt_detectRTLLanguage()?Qt::RightToLeft:Qt::LeftToRight); + switch (e->type()) { + case QEvent::LanguageChange: + // if the layout direction was set explicitly, then don't override it here + if (layout_direction == Qt::LayoutDirectionAuto) + setLayoutDirection(layout_direction); for (auto *topLevelWindow : QGuiApplication::topLevelWindows()) { if (topLevelWindow->flags() != Qt::Desktop) postEvent(topLevelWindow, new QEvent(QEvent::LanguageChange)); } - } else if (e->type() == QEvent::ApplicationFontChange || - e->type() == QEvent::ApplicationPaletteChange) { + break; + case QEvent::ApplicationFontChange: + case QEvent::ApplicationPaletteChange: for (auto *topLevelWindow : QGuiApplication::topLevelWindows()) { if (topLevelWindow->flags() != Qt::Desktop) postEvent(topLevelWindow, new QEvent(e->type())); } - } else if (e->type() == QEvent::Quit) { + break; + case QEvent::Quit: // Close open windows. This is done in order to deliver de-expose // events while the event loop is still running. for (QWindow *topLevelWindow : QGuiApplication::topLevelWindows()) { @@ -1959,8 +2027,10 @@ bool QGuiApplication::event(QEvent *e) return true; } } + break; + default: + break; } - return QCoreApplication::event(e); } @@ -1991,8 +2061,9 @@ bool QGuiApplicationPrivate::processNativeEvent(QWindow *window, const QByteArra return window->nativeEvent(eventType, message, result); } -void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) +void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) { + Q_TRACE_PARAM_REPLACE(QWindowSystemInterfacePrivate::WindowSystemEvent *, int); Q_TRACE_SCOPE(QGuiApplicationPrivate_processWindowSystemEvent, e->type); switch(e->type) { @@ -2017,8 +2088,8 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv case QWindowSystemInterfacePrivate::Leave: QGuiApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e)); break; - case QWindowSystemInterfacePrivate::ActivatedWindow: - QGuiApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e)); + case QWindowSystemInterfacePrivate::FocusWindow: + QGuiApplicationPrivate::processFocusWindowEvent(static_cast<QWindowSystemInterfacePrivate::FocusWindowEvent *>(e)); break; case QWindowSystemInterfacePrivate::WindowStateChanged: QGuiApplicationPrivate::processWindowStateChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowStateChangedEvent *>(e)); @@ -2026,6 +2097,9 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv case QWindowSystemInterfacePrivate::WindowScreenChanged: QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowScreenChangedEvent *>(e)); break; + case QWindowSystemInterfacePrivate::WindowDevicePixelRatioChanged: + QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *>(e)); + break; case QWindowSystemInterfacePrivate::SafeAreaMarginsChanged: QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(static_cast<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *>(e)); break; @@ -2173,22 +2247,24 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo QPointF localPoint = e->localPos; bool doubleClick = false; auto persistentEPD = devPriv->pointById(0); - const auto &persistentPoint = persistentEPD->eventPoint; if (mouseMove) { QGuiApplicationPrivate::lastCursorPosition = globalPoint; const auto doubleClickDistance = (e->device && e->device->type() == QInputDevice::DeviceType::Mouse ? mouseDoubleClickDistance : touchDoubleTapDistance); - const auto pressPos = persistentPoint.globalPressPosition(); + const auto pressPos = persistentEPD->eventPoint.globalPressPosition(); if (qAbs(globalPoint.x() - pressPos.x()) > doubleClickDistance || qAbs(globalPoint.y() - pressPos.y()) > doubleClickDistance) mousePressButton = Qt::NoButton; } else { + static unsigned long lastPressTimestamp = 0; mouse_buttons = e->buttons; if (mousePress) { ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval()); - doubleClick = e->timestamp - persistentPoint.pressTimestamp() < doubleClickInterval && button == mousePressButton; + doubleClick = e->timestamp - lastPressTimestamp + < doubleClickInterval && button == mousePressButton; mousePressButton = button; + lastPressTimestamp = e ->timestamp; } } @@ -2231,9 +2307,11 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo #endif QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, e->buttons, e->modifiers, e->source, device); + Q_ASSERT(devPriv->pointById(0) == persistentEPD); // we don't expect reallocation in QPlatformCursor::pointerEvenmt() // restore globalLastPosition to avoid invalidating the velocity calculations, // because the QPlatformCursor mouse event above was in native coordinates QMutableEventPoint::setGlobalLastPosition(persistentEPD->eventPoint, lastGlobalPosition); + persistentEPD = nullptr; // incoming and synth events can cause reallocation during delivery, so don't use this again // ev now contains a detached copy of the QEventPoint from QPointingDevicePrivate::activePoints ev.setTimestamp(e->timestamp); if (window->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) { @@ -2290,8 +2368,10 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo } } if (type == QEvent::MouseButtonRelease && e->buttons == Qt::NoButton) { - ev.setExclusiveGrabber(persistentPoint, nullptr); - ev.clearPassiveGrabbers(persistentPoint); + if (auto *persistentEPD = devPriv->queryPointById(0)) { + ev.setExclusiveGrabber(persistentEPD->eventPoint, nullptr); + ev.clearPassiveGrabbers(persistentEPD->eventPoint); + } } } @@ -2324,6 +2404,7 @@ void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wh mouse_buttons, e->modifiers, e->phase, e->inverted, e->source, device); ev.setTimestamp(e->timestamp); QGuiApplication::sendSpontaneousEvent(window, &ev); + e->eventAccepted = ev.isAccepted(); #else Q_UNUSED(e); #endif // QT_CONFIG(wheelevent) @@ -2431,10 +2512,10 @@ void QGuiApplicationPrivate::processLeaveEvent(QWindowSystemInterfacePrivate::Le QCoreApplication::sendSpontaneousEvent(e->leave.data(), &event); } -void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e) +void QGuiApplicationPrivate::processFocusWindowEvent(QWindowSystemInterfacePrivate::FocusWindowEvent *e) { QWindow *previous = QGuiApplicationPrivate::focus_window; - QWindow *newFocus = e->activated.data(); + QWindow *newFocus = e->focused.data(); if (previous == newFocus) return; @@ -2484,8 +2565,15 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate if (self) { self->notifyActiveWindowChange(previous); - if (previousFocusObject != qApp->focusObject()) + if (previousFocusObject != qApp->focusObject() || + // We are getting an activation change but there is no new focusObject, and we also + // don't have a previousFocusObject in the previously active window anymore. This can + // happen when window gets destroyed (see QWidgetWindow::focusObject returning nullptr + // when already in the QWidget destructor), so update the focusObject to avoid dangling + // pointers. See also QWidget::clearFocus(), which tries to cover for this as well. + (previous && previousFocusObject == nullptr && qApp->focusObject() == nullptr)) { self->_q_updateFocusObject(qApp->focusObject()); + } } emit qApp->focusWindowChanged(newFocus); @@ -2497,37 +2585,46 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate void QGuiApplicationPrivate::processWindowStateChangedEvent(QWindowSystemInterfacePrivate::WindowStateChangedEvent *wse) { - if (QWindow *window = wse->window.data()) { + if (QWindow *window = wse->window.data()) { + QWindowPrivate *windowPrivate = qt_window_private(window); + const auto originalEffectiveState = QWindowPrivate::effectiveState(windowPrivate->windowState); + + windowPrivate->windowState = wse->newState; + const auto newEffectiveState = QWindowPrivate::effectiveState(windowPrivate->windowState); + if (newEffectiveState != originalEffectiveState) + emit window->windowStateChanged(newEffectiveState); + + windowPrivate->updateVisibility(); + QWindowStateChangeEvent e(wse->oldState); - window->d_func()->windowState = wse->newState; QGuiApplication::sendSpontaneousEvent(window, &e); } } void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterfacePrivate::WindowScreenChangedEvent *wse) { - if (QWindow *window = wse->window.data()) { - if (window->screen() == wse->screen.data()) - return; - if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) { - if (QScreen *screen = wse->screen.data()) - topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */); - else // Fall back to default behavior, and try to find some appropriate screen - topLevelWindow->setScreen(nullptr); - } + QWindow *window = wse->window.data(); + if (!window) + return; - // We may have changed scaling; trigger resize event if needed, - // except on Windows, where we send resize events during WM_DPICHANGED - // event handling. FIXME: unify DPI change handling across all platforms. -#ifndef Q_OS_WIN - if (window->handle()) { - QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window)); - processGeometryChangeEvent(&gce); - } -#endif + if (window->screen() == wse->screen.data()) + return; + + if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) { + if (QScreen *screen = wse->screen.data()) + topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */); + else // Fall back to default behavior, and try to find some appropriate screen + topLevelWindow->setScreen(nullptr); } } +void QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *wde) +{ + if (wde->window.isNull()) + return; + QWindowPrivate::get(wde->window)->updateDevicePixelRatio(); +} + void QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *wse) { if (wse->window.isNull()) @@ -2542,11 +2639,33 @@ void QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(QWindowSystemInt void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate::ThemeChangeEvent *tce) { if (self) - self->notifyThemeChanged(); - if (QWindow *window = tce->window.data()) { - QEvent e(QEvent::ThemeChange); - QGuiApplication::sendSpontaneousEvent(window, &e); + self->handleThemeChanged(); + + QIconPrivate::clearIconCache(); + + QEvent themeChangeEvent(QEvent::ThemeChange); + const QWindowList windows = tce->window ? QWindowList{tce->window} : window_list; + for (auto *window : windows) + QGuiApplication::sendSpontaneousEvent(window, &themeChangeEvent); +} + +void QGuiApplicationPrivate::handleThemeChanged() +{ + const auto newColorScheme = platformTheme() ? platformTheme()->colorScheme() + : Qt::ColorScheme::Unknown; + QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(newColorScheme); + + updatePalette(); + + QIconLoader::instance()->updateSystemTheme(); + QAbstractFileIconProviderPrivate::clearIconTypeCache(); + + if (!(applicationResourceFlags & ApplicationFontExplicitlySet)) { + const auto locker = qt_scoped_lock(applicationFontMutex); + clearFontUnlocked(); + initFontUnlocked(); } + initThemeHints(); } void QGuiApplicationPrivate::processGeometryChangeEvent(QWindowSystemInterfacePrivate::GeometryChangeEvent *e) @@ -2605,8 +2724,10 @@ void QGuiApplicationPrivate::processCloseEvent(QWindowSystemInterfacePrivate::Cl { if (e->window.isNull()) return; - if (e->window.data()->d_func()->blockedByModalWindow) { - // a modal window is blocking this window, don't allow close events through + if (e->window.data()->d_func()->blockedByModalWindow && !e->window.data()->d_func()->inClose) { + // a modal window is blocking this window, don't allow close events through, unless they + // originate from a call to QWindow::close. + e->eventAccepted = false; return; } @@ -2711,6 +2832,8 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T QWindowSystemInterfacePrivate::MouseEvent mouseEvent(window, e->timestamp, e->local, e->global, e->buttons, e->modifiers, button, mouseType, Qt::MouseEventNotSynthesized, false, device); mouseEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic; + qCDebug(lcPtrDispatch) << "synthesizing mouse from tablet event" << mouseType + << e->local << button << e->buttons << e->modifiers; processMouseEvent(&mouseEvent); } #else @@ -2789,6 +2912,9 @@ void QGuiApplicationPrivate::processContextMenuEvent(QWindowSystemInterfacePriva void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::TouchEvent *e) { + if (!QInputDevicePrivate::isRegistered(e->device)) + return; + modifier_buttons = e->modifiers; QPointingDevice *device = const_cast<QPointingDevice *>(static_cast<const QPointingDevice *>(e->device)); QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(device); @@ -2798,17 +2924,17 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To // Send the TouchCancel to all windows with active touches and clean up. QTouchEvent touchEvent(QEvent::TouchCancel, device, e->modifiers); touchEvent.setTimestamp(e->timestamp); - QSet<QWindow *> windowsNeedingCancel; + constexpr qsizetype Prealloc = decltype(devPriv->activePoints)::mapped_container_type::PreallocatedSize; + QMinimalVarLengthFlatSet<QWindow *, Prealloc> windowsNeedingCancel; for (auto &epd : devPriv->activePoints.values()) { if (QWindow *w = QMutableEventPoint::window(epd.eventPoint)) windowsNeedingCancel.insert(w); } - for (QSet<QWindow *>::const_iterator winIt = windowsNeedingCancel.constBegin(), - winItEnd = windowsNeedingCancel.constEnd(); winIt != winItEnd; ++winIt) { - QGuiApplication::sendSpontaneousEvent(*winIt, &touchEvent); - } + for (QWindow *w : windowsNeedingCancel) + QGuiApplication::sendSpontaneousEvent(w, &touchEvent); + if (!self->synthesizedMousePoints.isEmpty() && !e->synthetic()) { for (QHash<QWindow *, SynthesizedMouseData>::const_iterator synthIt = self->synthesizedMousePoints.constBegin(), synthItEnd = self->synthesizedMousePoints.constEnd(); synthIt != synthItEnd; ++synthIt) { @@ -2865,15 +2991,15 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To case QEventPoint::State::Released: if (Q_UNLIKELY(!window.isNull() && window != QMutableEventPoint::window(ep))) - qCWarning(lcPtrDispatch) << "delivering touch release to same window" - << QMutableEventPoint::window(ep) << "not" << window.data(); + qCDebug(lcPtrDispatch) << "delivering touch release to same window" + << QMutableEventPoint::window(ep) << "not" << window.data(); window = QMutableEventPoint::window(ep); break; default: // update or stationary if (Q_UNLIKELY(!window.isNull() && window != QMutableEventPoint::window(ep))) - qCWarning(lcPtrDispatch) << "delivering touch update to same window" - << QMutableEventPoint::window(ep) << "not" << window.data(); + qCDebug(lcPtrDispatch) << "delivering touch update to same window" + << QMutableEventPoint::window(ep) << "not" << window.data(); window = QMutableEventPoint::window(ep); break; } @@ -2957,7 +3083,7 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To QEvent::Type mouseEventType = QEvent::MouseMove; Qt::MouseButton button = Qt::NoButton; Qt::MouseButtons buttons = Qt::LeftButton; - if (eventType == QEvent::TouchBegin && m_fakeMouseSourcePointId < 0) { + if (eventType == QEvent::TouchBegin || m_fakeMouseSourcePointId < 0) { m_fakeMouseSourcePointId = touchEvent.point(0).id(); qCDebug(lcPtrDispatch) << "synthesizing mouse events from touchpoint" << m_fakeMouseSourcePointId; } @@ -3041,26 +3167,16 @@ void QGuiApplicationPrivate::processScreenGeometryChange(QWindowSystemInterfaceP if (!e->screen) return; - QScreen *s = e->screen.data(); - - bool geometryChanged = e->geometry != s->d_func()->geometry; - s->d_func()->geometry = e->geometry; + { + QScreen *s = e->screen.data(); + QScreenPrivate::UpdateEmitter updateEmitter(s); - bool availableGeometryChanged = e->availableGeometry != s->d_func()->availableGeometry; - s->d_func()->availableGeometry = e->availableGeometry; + // Note: The incoming geometries have already been scaled by QHighDpi + // in the QWSI layer, so we don't need to call updateGeometry() here. + s->d_func()->geometry = e->geometry; + s->d_func()->availableGeometry = e->availableGeometry; - const Qt::ScreenOrientation primaryOrientation = s->primaryOrientation(); - if (geometryChanged) s->d_func()->updatePrimaryOrientation(); - - s->d_func()->emitGeometryChangeSignals(geometryChanged, availableGeometryChanged); - - if (geometryChanged) { - emit s->physicalSizeChanged(s->physicalSize()); - emit s->logicalDotsPerInchChanged(s->logicalDotsPerInch()); - - if (s->primaryOrientation() != primaryOrientation) - emit s->primaryOrientationChanged(s->primaryOrientation()); } resetCachedDevicePixelRatio(); @@ -3077,11 +3193,16 @@ void QGuiApplicationPrivate::processScreenLogicalDotsPerInchChange(QWindowSystem if (!e->screen) return; - QScreen *s = e->screen.data(); - s->d_func()->logicalDpi = QDpi(e->dpiX, e->dpiY); + { + QScreen *s = e->screen.data(); + QScreenPrivate::UpdateEmitter updateEmitter(s); + s->d_func()->logicalDpi = QDpi(e->dpiX, e->dpiY); + s->d_func()->updateGeometry(); + } - emit s->logicalDotsPerInchChanged(s->logicalDotsPerInch()); - s->d_func()->updateGeometriesWithSignals(); + for (QWindow *window : QGuiApplication::allWindows()) + if (window->screen() == e->screen) + QWindowPrivate::get(window)->updateDevicePixelRatio(); resetCachedDevicePixelRatio(); } @@ -3141,6 +3262,16 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E const bool wasExposed = p->exposed; p->exposed = e->isExposed && window->screen(); + // We expect that the platform plugins send DevicePixelRatioChange events. + // As a fail-safe make a final check here to make sure the cached DPR value is + // always up to date before sending the expose event. + if (e->isExposed && !e->region.isEmpty()) { + const bool dprWasChanged = QWindowPrivate::get(window)->updateDevicePixelRatio(); + if (dprWasChanged) + qWarning() << "The cached device pixel ratio value was stale on window expose. " + << "Please file a QTBUG which explains how to reproduce."; + } + // We treat expose events for an already exposed window as paint events if (wasExposed && p->exposed && shouldSynthesizePaintEvents) { QPaintEvent paintEvent(e->region); @@ -3366,11 +3497,13 @@ bool QGuiApplicationPrivate::setPalette(const QPalette &palette) */ QPalette QGuiApplicationPrivate::basePalette() const { - return platformTheme() ? *platformTheme()->palette() : Qt::gray; + const auto pf = platformTheme(); + return pf && pf->palette() ? *pf->palette() : Qt::gray; } void QGuiApplicationPrivate::handlePaletteChanged(const char *className) { +#if QT_DEPRECATED_SINCE(6, 0) if (!className) { Q_ASSERT(app_pal); QT_WARNING_PUSH @@ -3378,6 +3511,9 @@ QT_WARNING_DISABLE_DEPRECATED emit qGuiApp->paletteChanged(*QGuiApplicationPrivate::app_pal); QT_WARNING_POP } +#else + Q_UNUSED(className); +#endif // QT_DEPRECATED_SINCE(6, 0) if (is_app_running && !is_app_closing) { QEvent event(QEvent::ApplicationPaletteChange); @@ -3436,10 +3572,14 @@ void QGuiApplication::setFont(const QFont &font) if (emitChange && qGuiApp) { auto font = *QGuiApplicationPrivate::app_font; locker.unlock(); +#if QT_DEPRECATED_SINCE(6, 0) QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED emit qGuiApp->fontChanged(font); QT_WARNING_POP +#else + Q_UNUSED(font); +#endif // QT_DEPRECATED_SINCE(6, 0) QEvent event(QEvent::ApplicationFontChange); QGuiApplication::sendEvent(qGuiApp, &event); } @@ -3525,9 +3665,13 @@ void QGuiApplicationPrivate::notifyWindowIconChanged() The default is \c true. - If this property is \c true, the applications quits when the last visible - \l{Primary and Secondary Windows}{primary window} (i.e. top level window - with no transient parent) is closed. + If this property is \c true, the application will attempt to + quit when the last visible \l{Primary and Secondary Windows}{primary window} + (i.e. top level window with no transient parent) is closed. + + Note that attempting a quit may not necessarily result in the + application quitting, for example if there still are active + QEventLoopLocker instances, or the QEvent::Quit event is ignored. \sa quit(), QWindow::close() */ @@ -3583,7 +3727,13 @@ bool QGuiApplicationPrivate::lastWindowClosed() const bool QGuiApplicationPrivate::canQuitAutomatically() { - if (quitOnLastWindowClosed && !lastWindowClosed()) + // The automatic quit functionality is triggered by + // both QEventLoopLocker and maybeLastWindowClosed. + // Although the former is a QCoreApplication feature + // we don't want to quit the application when there + // are open windows, regardless of whether the app + // also quits automatically on maybeLastWindowClosed. + if (!lastWindowClosed()) return false; return QCoreApplicationPrivate::canQuitAutomatically(); @@ -3647,6 +3797,8 @@ Qt::ApplicationState QGuiApplication::applicationState() */ void QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy) { + if (qApp) + qWarning("setHighDpiScaleFactorRoundingPolicy must be called before creating the QGuiApplication instance"); QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = policy; } @@ -3867,7 +4019,8 @@ void QGuiApplication::sync() \property QGuiApplication::layoutDirection \brief the default layout direction for this application - On system start-up, the default layout direction depends on the + On system start-up, or when the direction is explicitly set to + Qt::LayoutDirectionAuto, the default layout direction depends on the application's language. The notifier signal was introduced in Qt 5.4. @@ -3877,11 +4030,15 @@ void QGuiApplication::sync() void QGuiApplication::setLayoutDirection(Qt::LayoutDirection direction) { - if (layout_direction == direction || direction == Qt::LayoutDirectionAuto) - return; - layout_direction = direction; + if (direction == Qt::LayoutDirectionAuto) + direction = qt_detectRTLLanguage() ? Qt::RightToLeft : Qt::LeftToRight; + + // no change to the explicitly set or auto-detected layout direction + if (direction == effective_layout_direction) + return; + effective_layout_direction = direction; if (qGuiApp) { emit qGuiApp->layoutDirectionChanged(direction); QGuiApplicationPrivate::self->notifyLayoutDirectionChange(); @@ -3890,10 +4047,15 @@ void QGuiApplication::setLayoutDirection(Qt::LayoutDirection direction) Qt::LayoutDirection QGuiApplication::layoutDirection() { - // layout_direction is only ever Qt::LayoutDirectionAuto if setLayoutDirection - // was never called, or called with Qt::LayoutDirectionAuto (which is a no-op). - // In that case we return the default LeftToRight. - return layout_direction == Qt::LayoutDirectionAuto ? Qt::LeftToRight : layout_direction; + /* + effective_layout_direction defaults to Qt::LeftToRight, and is updated with what is + auto-detected by a call to setLayoutDirection(Qt::LayoutDirectionAuto). This happens in + QGuiApplicationPrivate::init and when the language changes (or before if the application + calls the static function, but then no translators are installed so the auto-detection + always yields Qt::LeftToRight). + So we can be certain that it's always the right value. + */ + return effective_layout_direction; } /*! @@ -4117,7 +4279,10 @@ QInputMethod *QGuiApplication::inputMethod() /*! \fn void QGuiApplication::fontDatabaseChanged() - This signal is emitted when application fonts are loaded or removed. + This signal is emitted when the available fonts have changed. + + This can happen when application fonts are added or removed, or when the + system fonts change. \sa QFontDatabase::addApplicationFont(), QFontDatabase::addApplicationFontFromData(), @@ -4131,18 +4296,12 @@ QPixmap QGuiApplicationPrivate::getPixmapCursor(Qt::CursorShape cshape) return QPixmap(); } -void QGuiApplicationPrivate::notifyThemeChanged() +QPoint QGuiApplicationPrivate::QLastCursorPosition::toPoint() const noexcept { - updatePalette(); - - QAbstractFileIconProviderPrivate::clearIconTypeCache(); - - if (!(applicationResourceFlags & ApplicationFontExplicitlySet)) { - const auto locker = qt_scoped_lock(applicationFontMutex); - clearFontUnlocked(); - initFontUnlocked(); - } - initThemeHints(); + // Guard against the default initialization of qInf() (avoid UB or SIGFPE in conversion). + if (Q_UNLIKELY(qIsInf(thePoint.x()))) + return QPoint(std::numeric_limits<int>::max(), std::numeric_limits<int>::max()); + return thePoint.toPoint(); } #if QT_CONFIG(draganddrop) @@ -4230,10 +4389,13 @@ void *QGuiApplication::resolveInterface(const char *name, int revision) const #if QT_CONFIG(xcb) QT_NATIVE_INTERFACE_RETURN_IF(QX11Application, platformNativeInterface()); #endif +#if QT_CONFIG(wayland) + QT_NATIVE_INTERFACE_RETURN_IF(QWaylandApplication, platformNativeInterface()); +#endif return QCoreApplication::resolveInterface(name, revision); } -#include "moc_qguiapplication.cpp" - QT_END_NAMESPACE + +#include "moc_qguiapplication.cpp" |