diff --git a/include/GuiApplication.h b/include/GuiApplication.h index 3a0851499..16680934f 100644 --- a/include/GuiApplication.h +++ b/include/GuiApplication.h @@ -26,6 +26,7 @@ #define LMMS_GUI_GUI_APPLICATION_H #include +#include #include "lmms_export.h" #include "lmmsconfig.h" @@ -53,10 +54,13 @@ public: ~GuiApplication() override; static GuiApplication* instance(); + static void sigintHandler(int); #ifdef LMMS_BUILD_WIN32 static QFont getWin32SystemFont(); #endif + void createSocketNotifier(); + MainWindow* mainWindow() { return m_mainWindow; } MixerView* mixerView() { return m_mixerView; } SongEditorWindow* songEditor() { return m_songEditor; } @@ -67,11 +71,15 @@ public: AutomationEditorWindow* automationEditor() { return m_automationEditor; } ControllerRackView* getControllerRackView() { return m_controllerRackView; } + //! File descriptors for unix socketpair, used to receive SIGINT + static inline int s_sigintFd[2]; + public slots: void displayInitProgress(const QString &msg); private slots: void childDestroyed(QObject *obj); + void sigintOccurred(); private: static GuiApplication* s_instance; @@ -86,6 +94,7 @@ private: MicrotunerConfig* m_microtunerConfig; ControllerRackView* m_controllerRackView; QLabel* m_loadingProgressLabel; + QSocketNotifier* m_sigintNotifier; }; // Short-hand function diff --git a/src/core/main.cpp b/src/core/main.cpp index c75e91ccd..fb54feeab 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -55,7 +55,7 @@ #include #endif -#include +#include // To register the signal handler #include "MainApplication.h" #include "ConfigManager.h" @@ -76,12 +76,12 @@ #include // For feenableexcept #include // For backtrace and backtrace_symbols_fd #include // For STDERR_FILENO -#include // To register the signal handler #endif #ifdef LMMS_DEBUG_FPE -void signalHandler( int signum ) { +void sigfpeHandler(int signum) +{ // Get a back trace void *array[10]; @@ -315,8 +315,9 @@ int main( int argc, char * * argv ) // Install the trap handler // register signal SIGFPE and signal handler - signal(SIGFPE, signalHandler); + signal(SIGFPE, sigfpeHandler); #endif + signal(SIGINT, gui::GuiApplication::sigintHandler); #ifdef LMMS_BUILD_WIN32 // Don't touch redirected streams here diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index 8c674112d..7e62a3141 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -41,14 +41,22 @@ #include "SongEditor.h" #include +#include #include #include #include #include #include +#include +#include #ifdef LMMS_BUILD_WIN32 +#include +#include #include +#else +#include +#include #endif namespace lmms @@ -75,6 +83,9 @@ GuiApplication* GuiApplication::instance() GuiApplication::GuiApplication() { + // Immediately register our SIGINT handler + createSocketNotifier(); + // prompt the user to create the LMMS working directory (e.g. ~/Documents/lmms) if it doesn't exist if ( !ConfigManager::inst()->hasWorkingDir() && QMessageBox::question( nullptr, @@ -240,6 +251,52 @@ void GuiApplication::childDestroyed(QObject *obj) } } +/** \brief Called from main when SIGINT is fired + * + * Unix signal handlers can only call async-signal-safe functions: + * write(fd) --> QSocketNotifier --> SLOT sigintOccurred() + * + * See https://doc.qt.io/qt-6/unix-signals.html + */ +void GuiApplication::sigintHandler(int) +{ +#ifdef LMMS_BUILD_WIN32 + char message[] = "Sorry, SIGINT is unhandled on this platform\n"; + std::ignore = _write(_fileno(stderr), message, sizeof(message)); +#else + char a = 1; + std::ignore = ::write(s_sigintFd[0], &a, sizeof(a)); +#endif +} + +// Create our unix signal notifiers +void GuiApplication::createSocketNotifier() +{ +#ifdef LMMS_BUILD_WIN32 + // no-op +#else + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, s_sigintFd)) + { + qFatal("Couldn't create SIGINT socketpair"); + return; + } + + // Listen on the file descriptor for SIGINT + m_sigintNotifier = new QSocketNotifier(s_sigintFd[1], QSocketNotifier::Read, this); + connect(m_sigintNotifier, SIGNAL(activated(QSocketDescriptor)), this, SLOT(sigintOccurred()), Qt::QueuedConnection); +#endif +} + +// Handle the shutdown event +void GuiApplication::sigintOccurred() +{ + m_sigintNotifier->setEnabled(false); + qDebug() << "Shutting down..."; + // cleanup, etc + qApp->exit(3); + m_sigintNotifier->setEnabled(true); +} + #ifdef LMMS_BUILD_WIN32 /*! * @brief Returns the Windows System font.