diff options
Diffstat (limited to 'src/gui/kernel/qguiapplication.cpp')
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 420 |
1 files changed, 274 insertions, 146 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index dbe50aee82..c97374e975 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -9,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> @@ -16,12 +17,15 @@ #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> @@ -41,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> @@ -49,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" @@ -93,12 +101,14 @@ #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(...) \ @@ -226,7 +236,7 @@ static void initThemeHints() static bool checkNeedPortalSupport() { #if QT_CONFIG(dbus) - return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, "flatpak-info"_L1).isEmpty() || qEnvironmentVariableIsSet("SNAP"); + return QFileInfo::exists("/.flatpak-info"_L1) || qEnvironmentVariableIsSet("SNAP"); #else return false; #endif // QT_CONFIG(dbus) @@ -253,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); @@ -554,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 @@ -569,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 @@ -593,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. @@ -682,6 +695,7 @@ QGuiApplication::~QGuiApplication() QGuiApplicationPrivate::lastCursorPosition.reset(); QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr; QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive; + QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = Qt::HighDpiScaleFactorRoundingPolicy::PassThrough; QGuiApplicationPrivate::currentDragWindow = nullptr; QGuiApplicationPrivate::tabletDevicePoints.clear(); } @@ -736,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 @@ -756,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() @@ -782,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) @@ -833,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); } @@ -845,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 @@ -858,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: @@ -914,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() { @@ -1098,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; @@ -1145,19 +1189,31 @@ 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"); @@ -1192,6 +1248,10 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString 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."; @@ -1213,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(", "_L1)); + 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 @@ -1246,7 +1306,7 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString 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) { @@ -1258,7 +1318,7 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString // 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) { @@ -1279,8 +1339,8 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString // 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(u'='); + 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 = @@ -1293,14 +1353,14 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString 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(QLatin1StringView(pluginSpec), QString()); @@ -1470,6 +1530,8 @@ void QGuiApplicationPrivate::createPlatformIntegration() Q_UNUSED(platformExplicitlySelected); 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); @@ -1488,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(); @@ -1502,7 +1568,7 @@ void QGuiApplicationPrivate::eventDispatcherReady() platform_integration->initialize(); } -void QGuiApplicationPrivate::init() +void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::init() { Q_TRACE_SCOPE(QGuiApplicationPrivate_init); @@ -1565,7 +1631,7 @@ void QGuiApplicationPrivate::init() ++i; if (argv[i] && *argv[i]) { session_id = QString::fromLatin1(argv[i]); - int p = session_id.indexOf(u'_'); + qsizetype p = session_id.indexOf(u'_'); if (p >= 0) { session_key = session_id.mid(p +1); session_id = session_id.left(p); @@ -1676,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(); @@ -1781,7 +1847,7 @@ Qt::KeyboardModifiers QGuiApplication::queryKeyboardModifiers() { CHECK_QAPP_INSTANCE(Qt::KeyboardModifiers{}) QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration(); - return pi->queryKeyboardModifiers(); + return pi->keyMapper()->queryKeyboardModifiers(); } /*! @@ -1839,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 @@ -1931,7 +1998,8 @@ bool QGuiApplication::notify(QObject *object, QEvent *event) */ bool QGuiApplication::event(QEvent *e) { - if (e->type() == QEvent::LanguageChange) { + 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); @@ -1939,13 +2007,15 @@ bool QGuiApplication::event(QEvent *e) 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()) { @@ -1957,8 +2027,10 @@ bool QGuiApplication::event(QEvent *e) return true; } } + break; + default: + break; } - return QCoreApplication::event(e); } @@ -1989,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) { @@ -2015,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)); @@ -2024,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; @@ -2181,12 +2257,14 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo 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 - persistentEPD->eventPoint.pressTimestamp() + doubleClick = e->timestamp - lastPressTimestamp < doubleClickInterval && button == mousePressButton; mousePressButton = button; + lastPressTimestamp = e ->timestamp; } } @@ -2326,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) @@ -2433,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; @@ -2506,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()) @@ -2553,6 +2641,8 @@ void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate:: if (self) self->handleThemeChanged(); + QIconPrivate::clearIconCache(); + QEvent themeChangeEvent(QEvent::ThemeChange); const QWindowList windows = tce->window ? QWindowList{tce->window} : window_list; for (auto *window : windows) @@ -2561,8 +2651,13 @@ void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate:: 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)) { @@ -2629,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; } @@ -2735,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 @@ -2825,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) { @@ -2892,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; } @@ -3068,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(); @@ -3104,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(); } @@ -3168,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); @@ -3393,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 @@ -3405,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); @@ -3463,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); } @@ -3552,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() */ @@ -3610,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(); @@ -3674,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; } @@ -4264,6 +4389,9 @@ 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); } |