File fix_quick_exit.patch of Package spectacle
From e5f1a6ef499d4569db8dc2ddd0a282caa6cf7c60 Mon Sep 17 00:00:00 2001
From: Noah Davis <noahadvs@gmail.com>
Date: Thu, 5 Mar 2026 13:05:24 -0500
Subject: [PATCH] Fix crash on quit with a quickly selected region
If you quickly selected a region and quickly pressed escape to quit, it could cause a crash.
BUG: 517064
FIXED-IN: 6.6.3
(cherry picked from commit 3dff870b1628c29a0bbbab0fa44ebd0515769524)
Co-authored-by: Noah Davis <noahadvs@gmail.com>
---
src/Gui/ExportMenu.cpp | 21 +++++++++++++++------
src/Gui/ExportMenu.h | 1 +
src/Gui/HelpMenu.cpp | 21 +++++++++++++++------
src/Gui/HelpMenu.h | 1 +
src/Gui/OptionsMenu.cpp | 21 +++++++++++++++------
src/Gui/OptionsMenu.h | 1 +
src/Gui/RecordingModeMenu.cpp | 21 +++++++++++++++------
src/Gui/RecordingModeMenu.h | 1 +
src/Gui/ScreenshotModeMenu.cpp | 21 +++++++++++++++------
src/Gui/ScreenshotModeMenu.h | 1 +
10 files changed, 80 insertions(+), 30 deletions(-)
diff --git a/src/Gui/ExportMenu.cpp b/src/Gui/ExportMenu.cpp
index cf8a98a6c..a1a361315 100644
--- a/src/Gui/ExportMenu.cpp
+++ b/src/Gui/ExportMenu.cpp
@@ -33,7 +33,7 @@
using namespace std::chrono_literals;
using namespace Qt::StringLiterals;
-static std::unique_ptr<ExportMenu> s_instance = nullptr;
+static ExportMenu *s_instance = nullptr;
ExportMenu::ExportMenu(QWidget *parent)
: SpectacleMenu(parent)
@@ -60,16 +60,25 @@ ExportMenu::ExportMenu(QWidget *parent)
getKServiceItems();
}
+ExportMenu::~ExportMenu()
+{
+ s_instance = nullptr;
+}
+
ExportMenu *ExportMenu::instance()
{
if (!s_instance && SpectacleCore::instance()) {
- s_instance = std::unique_ptr<ExportMenu>(new ExportMenu);
- // We have to destroy this after SpectacleCore to prevent a crash from the Qt Quick UI.
- connect(SpectacleCore::instance(), &QObject::destroyed, s_instance.get(), [] {
- s_instance.reset();
+ s_instance = new ExportMenu;
+ // We have to destroy this after SpectacleCore to ensure that destructors
+ // are called. We don't just rely on smart pointers because they won't delete
+ // the menus at the right time and cause a crash while quitting.
+ connect(SpectacleCore::instance(), &QObject::destroyed, qApp, [] {
+ if (s_instance) {
+ delete s_instance;
+ }
});
}
- return s_instance.get();
+ return s_instance;
}
void ExportMenu::onImageChanged()
diff --git a/src/Gui/ExportMenu.h b/src/Gui/ExportMenu.h
index 192b9d315..688abad76 100644
--- a/src/Gui/ExportMenu.h
+++ b/src/Gui/ExportMenu.h
@@ -47,6 +47,7 @@ Q_SIGNALS:
private:
explicit ExportMenu(QWidget *parent = nullptr);
+ ~ExportMenu();
Q_SLOT void onImageChanged();
Q_SLOT void openScreenshotsFolder();
diff --git a/src/Gui/HelpMenu.cpp b/src/Gui/HelpMenu.cpp
index e78a65c46..9cd2d8050 100644
--- a/src/Gui/HelpMenu.cpp
+++ b/src/Gui/HelpMenu.cpp
@@ -17,7 +17,7 @@
using namespace Qt::StringLiterals;
-static std::unique_ptr<HelpMenu> s_instance = nullptr;
+static HelpMenu *s_instance = nullptr;
static QObject *findWidgetOfType(const char *className)
{
@@ -43,16 +43,25 @@ HelpMenu::HelpMenu(QWidget* parent)
connect(this, &QMenu::triggered, this, &HelpMenu::onTriggered);
}
+HelpMenu::~HelpMenu()
+{
+ s_instance = nullptr;
+}
+
HelpMenu *HelpMenu::instance()
{
if (!s_instance && SpectacleCore::instance()) {
- s_instance = std::unique_ptr<HelpMenu>(new HelpMenu);
- // We have to destroy this after SpectacleCore to prevent a crash from the Qt Quick UI.
- connect(SpectacleCore::instance(), &QObject::destroyed, s_instance.get(), [] {
- s_instance.reset();
+ s_instance = new HelpMenu;
+ // We have to destroy this after SpectacleCore to ensure that destructors
+ // are called. We don't just rely on smart pointers because they won't delete
+ // the menus at the right time and cause a crash while quitting.
+ connect(SpectacleCore::instance(), &QObject::destroyed, qApp, [] {
+ if (s_instance) {
+ delete s_instance;
+ }
});
}
- return s_instance.get();
+ return s_instance;
}
void HelpMenu::showAppHelp()
diff --git a/src/Gui/HelpMenu.h b/src/Gui/HelpMenu.h
index 475ca8a64..81d760379 100644
--- a/src/Gui/HelpMenu.h
+++ b/src/Gui/HelpMenu.h
@@ -34,6 +34,7 @@ public:
private:
explicit HelpMenu(QWidget *parent = nullptr);
+ ~HelpMenu();
Q_SLOT void onTriggered(QAction *action);
const std::unique_ptr<KHelpMenu> kHelpMenu;
friend class HelpMenuSingleton;
diff --git a/src/Gui/OptionsMenu.cpp b/src/Gui/OptionsMenu.cpp
index 8a70f20ad..25fa1096a 100644
--- a/src/Gui/OptionsMenu.cpp
+++ b/src/Gui/OptionsMenu.cpp
@@ -23,7 +23,7 @@
using namespace Qt::StringLiterals;
-static std::unique_ptr<OptionsMenu> s_instance = nullptr;
+static OptionsMenu *s_instance = nullptr;
OptionsMenu::OptionsMenu(QWidget *parent)
: SpectacleMenu(parent)
@@ -152,16 +152,25 @@ OptionsMenu::OptionsMenu(QWidget *parent)
});
}
+OptionsMenu::~OptionsMenu()
+{
+ s_instance = nullptr;
+}
+
OptionsMenu *OptionsMenu::instance()
{
if (!s_instance && SpectacleCore::instance()) {
- s_instance = std::unique_ptr<OptionsMenu>(new OptionsMenu);
- // We have to destroy this after SpectacleCore to prevent a crash from the Qt Quick UI.
- connect(SpectacleCore::instance(), &QObject::destroyed, s_instance.get(), [] {
- s_instance.reset();
+ s_instance = new OptionsMenu;
+ // We have to destroy this after SpectacleCore to ensure that destructors
+ // are called. We don't just rely on smart pointers because they won't delete
+ // the menus at the right time and cause a crash while quitting.
+ connect(SpectacleCore::instance(), &QObject::destroyed, qApp, [] {
+ if (s_instance) {
+ delete s_instance;
+ }
});
}
- return s_instance.get();
+ return s_instance;
}
void OptionsMenu::showPreferencesDialog()
diff --git a/src/Gui/OptionsMenu.h b/src/Gui/OptionsMenu.h
index b600f3f4c..a000186a0 100644
--- a/src/Gui/OptionsMenu.h
+++ b/src/Gui/OptionsMenu.h
@@ -42,6 +42,7 @@ protected:
void mouseReleaseEvent(QMouseEvent *event) override;
explicit OptionsMenu(QWidget *parent = nullptr);
+ ~OptionsMenu();
void delayActionLayoutUpdate();
const std::unique_ptr<QWidgetAction> m_delayAction;
diff --git a/src/Gui/RecordingModeMenu.cpp b/src/Gui/RecordingModeMenu.cpp
index 425f28d9e..981f9c9b6 100644
--- a/src/Gui/RecordingModeMenu.cpp
+++ b/src/Gui/RecordingModeMenu.cpp
@@ -10,7 +10,7 @@
using namespace Qt::StringLiterals;
-static std::unique_ptr<RecordingModeMenu> s_instance = nullptr;
+static RecordingModeMenu *s_instance = nullptr;
RecordingModeMenu::RecordingModeMenu(QWidget *parent)
: SpectacleMenu(i18nc("@title:menu", "Recording Modes"), parent)
@@ -54,16 +54,25 @@ RecordingModeMenu::RecordingModeMenu(QWidget *parent)
connect(RecordingModeModel::instance(), &RecordingModeModel::recordingModesChanged, this, addModes);
}
+RecordingModeMenu::~RecordingModeMenu()
+{
+ s_instance = nullptr;
+}
+
RecordingModeMenu *RecordingModeMenu::instance()
{
if (!s_instance && SpectacleCore::instance()) {
- s_instance = std::unique_ptr<RecordingModeMenu>(new RecordingModeMenu);
- // We have to destroy this after SpectacleCore to prevent a crash from the Qt Quick UI.
- connect(SpectacleCore::instance(), &QObject::destroyed, s_instance.get(), [] {
- s_instance.reset();
+ s_instance = new RecordingModeMenu;
+ // We have to destroy this after SpectacleCore to ensure that destructors
+ // are called. We don't just rely on smart pointers because they won't delete
+ // the menus at the right time and cause a crash while quitting.
+ connect(SpectacleCore::instance(), &QObject::destroyed, qApp, [] {
+ if (s_instance) {
+ delete s_instance;
+ }
});
}
- return s_instance.get();
+ return s_instance;
}
#include "moc_RecordingModeMenu.cpp"
diff --git a/src/Gui/RecordingModeMenu.h b/src/Gui/RecordingModeMenu.h
index fe8f0a712..c45ca3c80 100644
--- a/src/Gui/RecordingModeMenu.h
+++ b/src/Gui/RecordingModeMenu.h
@@ -27,4 +27,5 @@ public:
private:
explicit RecordingModeMenu(QWidget *parent = nullptr);
+ ~RecordingModeMenu();
};
diff --git a/src/Gui/ScreenshotModeMenu.cpp b/src/Gui/ScreenshotModeMenu.cpp
index c557c2169..b27900f02 100644
--- a/src/Gui/ScreenshotModeMenu.cpp
+++ b/src/Gui/ScreenshotModeMenu.cpp
@@ -10,7 +10,7 @@
using namespace Qt::StringLiterals;
-static std::unique_ptr<ScreenshotModeMenu> s_instance = nullptr;
+static ScreenshotModeMenu *s_instance = nullptr;
ScreenshotModeMenu::ScreenshotModeMenu(QWidget *parent)
: SpectacleMenu(i18nc("@title:menu", "Screenshot Modes"), parent)
@@ -63,16 +63,25 @@ ScreenshotModeMenu::ScreenshotModeMenu(QWidget *parent)
connect(CaptureModeModel::instance(), &CaptureModeModel::captureModesChanged, this, addModes);
}
+ScreenshotModeMenu::~ScreenshotModeMenu()
+{
+ s_instance = nullptr;
+}
+
ScreenshotModeMenu *ScreenshotModeMenu::instance()
{
if (!s_instance && SpectacleCore::instance()) {
- s_instance = std::unique_ptr<ScreenshotModeMenu>(new ScreenshotModeMenu);
- // We have to destroy this after SpectacleCore to prevent a crash from the Qt Quick UI.
- connect(SpectacleCore::instance(), &QObject::destroyed, s_instance.get(), [] {
- s_instance.reset();
+ s_instance = new ScreenshotModeMenu;
+ // We have to destroy this after SpectacleCore to ensure that destructors
+ // are called. We don't just rely on smart pointers because they won't delete
+ // the menus at the right time and cause a crash while quitting.
+ connect(SpectacleCore::instance(), &QObject::destroyed, qApp, [] {
+ if (s_instance) {
+ delete s_instance;
+ }
});
}
- return s_instance.get();
+ return s_instance;
}
#include "moc_ScreenshotModeMenu.cpp"
diff --git a/src/Gui/ScreenshotModeMenu.h b/src/Gui/ScreenshotModeMenu.h
index 387a05db0..9ce75fa50 100644
--- a/src/Gui/ScreenshotModeMenu.h
+++ b/src/Gui/ScreenshotModeMenu.h
@@ -27,4 +27,5 @@ public:
private:
explicit ScreenshotModeMenu(QWidget *parent = nullptr);
+ ~ScreenshotModeMenu();
};
--
GitLab