Propagate application termination requests through QPA
Instead of having each platform plugin deal with application termination in their own weird ways, we now have a QPA API to signal that the system requested the application to terminate. On the QGuiApplication side this results in a Quit event being sent to the application, which triggers the default behavior of closing all app windows, and then finally calling QCoreApplication::quit(). The quit event replaces the misuse of a close event being sent to the application. Close events are documented as being sent to windows. The close events that are sent to individual windows as part of the quit process can be ignored. This will skip the final quit() of the application, and also inform the platform that the quit was not accepted, in case that should be propagated further. In the future the logic for closing windows should be unified between the various approaches in closeAllWindows, shouldQuit, and friends. Change-Id: I0ed7f1c0d3f0bf1a755e1dd4066e1575fc3a28e1 Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
parent
2967510213
commit
1b6db18494
@ -1863,7 +1863,20 @@ bool QGuiApplication::event(QEvent *e)
|
||||
{
|
||||
if(e->type() == QEvent::LanguageChange) {
|
||||
setLayoutDirection(qt_detectRTLLanguage()?Qt::RightToLeft:Qt::LeftToRight);
|
||||
} else if (e->type() == 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()) {
|
||||
// Already closed windows will not have a platform window, skip those
|
||||
if (!topLevelWindow->handle())
|
||||
continue;
|
||||
if (!topLevelWindow->close()) {
|
||||
e->ignore();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QCoreApplication::event(e);
|
||||
}
|
||||
|
||||
@ -1940,6 +1953,9 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
|
||||
QWindowSystemInterfacePrivate::ApplicationStateChangedEvent * changeEvent = static_cast<QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *>(e);
|
||||
QGuiApplicationPrivate::setApplicationState(changeEvent->newState, changeEvent->forcePropagate); }
|
||||
break;
|
||||
case QWindowSystemInterfacePrivate::ApplicationTermination:
|
||||
QGuiApplicationPrivate::processApplicationTermination(e);
|
||||
break;
|
||||
case QWindowSystemInterfacePrivate::FlushEvents: {
|
||||
QWindowSystemInterfacePrivate::FlushEventsEvent *flushEventsEvent = static_cast<QWindowSystemInterfacePrivate::FlushEventsEvent *>(e);
|
||||
QWindowSystemInterface::deferredFlushWindowSystemEvents(flushEventsEvent->flags); }
|
||||
@ -3489,6 +3505,13 @@ bool QGuiApplicationPrivate::tryCloseRemainingWindows(QWindowList processedWindo
|
||||
return true;
|
||||
}
|
||||
|
||||
void QGuiApplicationPrivate::processApplicationTermination(QWindowSystemInterfacePrivate::WindowSystemEvent *windowSystemEvent)
|
||||
{
|
||||
QEvent event(QEvent::Quit);
|
||||
QGuiApplication::sendSpontaneousEvent(QGuiApplication::instance(), &event);
|
||||
windowSystemEvent->eventAccepted = event.isAccepted();
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.2
|
||||
\fn Qt::ApplicationState QGuiApplication::applicationState()
|
||||
|
@ -141,6 +141,8 @@ public:
|
||||
|
||||
static void processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e);
|
||||
|
||||
static void processApplicationTermination(QWindowSystemInterfacePrivate::WindowSystemEvent *e);
|
||||
|
||||
static void updateFilteredScreenOrientation(QScreen *screen);
|
||||
static void reportScreenOrientationChange(QScreen *screen);
|
||||
static void processScreenOrientationChange(QWindowSystemInterfacePrivate::ScreenOrientationEvent *e);
|
||||
|
@ -285,6 +285,12 @@ QT_DEFINE_QPA_EVENT_HANDLER(void, handleApplicationStateChanged, Qt::Application
|
||||
QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e);
|
||||
}
|
||||
|
||||
QT_DEFINE_QPA_EVENT_HANDLER(bool, handleApplicationTermination)
|
||||
{
|
||||
auto *e = new QWindowSystemInterfacePrivate::WindowSystemEvent(QWindowSystemInterfacePrivate::ApplicationTermination);
|
||||
return QWindowSystemInterfacePrivate::handleWindowSystemEvent<Delivery>(e);
|
||||
}
|
||||
|
||||
QWindowSystemInterfacePrivate::GeometryChangeEvent::GeometryChangeEvent(QWindow *window, const QRect &newGeometry)
|
||||
: WindowSystemEvent(GeometryChange)
|
||||
, window(window)
|
||||
|
@ -215,6 +215,9 @@ public:
|
||||
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
|
||||
static void handleApplicationStateChanged(Qt::ApplicationState newState, bool forcePropagate = false);
|
||||
|
||||
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
|
||||
static bool handleApplicationTermination();
|
||||
|
||||
#if QT_CONFIG(draganddrop)
|
||||
#if QT_DEPRECATED_SINCE(5, 11)
|
||||
QT_DEPRECATED static QPlatformDragQtResponse handleDrag(QWindow *window, const QMimeData *dropData,
|
||||
|
@ -99,7 +99,8 @@ public:
|
||||
ApplicationStateChanged = 0x19,
|
||||
FlushEvents = 0x20,
|
||||
WindowScreenChanged = 0x21,
|
||||
SafeAreaMarginsChanged = 0x22
|
||||
SafeAreaMarginsChanged = 0x22,
|
||||
ApplicationTermination = 0x23
|
||||
};
|
||||
|
||||
class WindowSystemEvent {
|
||||
|
@ -88,10 +88,13 @@
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
#include <qwindowdefs.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
Q_LOGGING_CATEGORY(lcQpaApplication, "qt.qpa.application");
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
@implementation QCocoaApplicationDelegate {
|
||||
bool startedQuit;
|
||||
NSObject <NSApplicationDelegate> *reflectionDelegate;
|
||||
bool inLaunch;
|
||||
}
|
||||
@ -150,30 +153,20 @@ QT_USE_NAMESPACE
|
||||
// No event loop is executing. This probably means that Qt is used as a plugin,
|
||||
// or as a part of a native Cocoa application. In any case it should be fine to
|
||||
// terminate now.
|
||||
qCDebug(lcQpaApplication) << "No running event loops, terminating now";
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
QCloseEvent ev;
|
||||
QGuiApplication::sendEvent(qGuiApp, &ev);
|
||||
if (!ev.isAccepted())
|
||||
if (!QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>()) {
|
||||
qCDebug(lcQpaApplication) << "Application termination canceled";
|
||||
return NSTerminateCancel;
|
||||
|
||||
if (!startedQuit) {
|
||||
startedQuit = true;
|
||||
// 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()) {
|
||||
// Already closed windows will not have a platform window, skip those
|
||||
if (!topLevelWindow->handle())
|
||||
continue;
|
||||
|
||||
QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(topLevelWindow);
|
||||
}
|
||||
|
||||
QGuiApplication::exit(0);
|
||||
startedQuit = false;
|
||||
}
|
||||
|
||||
// Even if the application termination was accepted by the application we can't
|
||||
// return NSTerminateNow, as that would trigger AppKit to ultimately call exit().
|
||||
// We need to ensure that the runloop continues spinning so that we can return
|
||||
// from our own event loop back to main(), and exit from there.
|
||||
qCDebug(lcQpaApplication) << "Termination accepted, but returning to runloop for exit through main()";
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QFileOpenEvent>
|
||||
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
|
||||
#include <Entry.h>
|
||||
#include <Path.h>
|
||||
|
||||
@ -52,8 +54,7 @@ QHaikuApplication::QHaikuApplication(const char *signature)
|
||||
|
||||
bool QHaikuApplication::QuitRequested()
|
||||
{
|
||||
QEvent quitEvent(QEvent::Quit);
|
||||
QCoreApplication::sendEvent(QCoreApplication::instance(), &quitEvent);
|
||||
QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,8 @@
|
||||
|
||||
#ifndef QT_NO_SESSIONMANAGER
|
||||
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
|
||||
#include <qguiapplication.h>
|
||||
#include <qdatetime.h>
|
||||
#include <qfileinfo.h>
|
||||
@ -289,8 +291,7 @@ static void sm_dieCallback(SmcConn smcConn, SmPointer /* clientData */)
|
||||
if (smcConn != smcConnection)
|
||||
return;
|
||||
resetSmState();
|
||||
QEvent quitEvent(QEvent::Quit);
|
||||
QGuiApplication::sendEvent(qApp, &quitEvent);
|
||||
QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
|
||||
}
|
||||
|
||||
static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData)
|
||||
|
@ -1866,22 +1866,19 @@ void QApplication::aboutQt()
|
||||
bool QApplication::event(QEvent *e)
|
||||
{
|
||||
Q_D(QApplication);
|
||||
if(e->type() == QEvent::Close) {
|
||||
QCloseEvent *ce = static_cast<QCloseEvent*>(e);
|
||||
ce->accept();
|
||||
if (e->type() == QEvent::Quit) {
|
||||
closeAllWindows();
|
||||
|
||||
const QWidgetList list = topLevelWidgets();
|
||||
for (auto *w : list) {
|
||||
for (auto *w : topLevelWidgets()) {
|
||||
if (w->isVisible() && !(w->windowType() == Qt::Desktop) && !(w->windowType() == Qt::Popup) &&
|
||||
(!(w->windowType() == Qt::Dialog) || !w->parentWidget())) {
|
||||
ce->ignore();
|
||||
break;
|
||||
e->ignore();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (ce->isAccepted()) {
|
||||
return true;
|
||||
}
|
||||
// Explicitly call QCoreApplication instead of QGuiApplication so that
|
||||
// we don't let QGuiApplication close any windows we skipped earlier in
|
||||
// closeAllWindows(). FIXME: Unify all this close magic through closeAllWindows.
|
||||
return QCoreApplication::event(e);
|
||||
#ifndef Q_OS_WIN
|
||||
} else if (e->type() == QEvent::LocaleChange) {
|
||||
// on Windows the event propagation is taken care by the
|
||||
|
Loading…
x
Reference in New Issue
Block a user