diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ed802d3290..f8bf112887 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -9,6 +9,10 @@ #ifdef __APPLE__ #include // for chdir #endif +#ifdef __linux__ +#include +#include +#endif // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. #include "applets/qt_controller.h" @@ -258,6 +262,10 @@ GMainWindow::GMainWindow() config{std::make_unique(*system)}, vfs{std::make_shared()}, provider{std::make_unique()} { +#ifdef __linux__ + SetupSigInterrupts(); +#endif + Common::Log::Initialize(); LoadTranslation(); @@ -461,6 +469,11 @@ GMainWindow::~GMainWindow() { if (render_window->parent() == nullptr) { delete render_window; } + +#ifdef __linux__ + ::close(sig_interrupt_fds[0]); + ::close(sig_interrupt_fds[1]); +#endif } void GMainWindow::RegisterMetaTypes() { @@ -1324,6 +1337,50 @@ static void ReleaseWakeLockLinux(QDBusObjectPath lock) { QString::fromLatin1("org.freedesktop.portal.Request")); unlocker.call(QString::fromLatin1("Close")); } + +std::array GMainWindow::sig_interrupt_fds{0, 0, 0}; + +void GMainWindow::SetupSigInterrupts() { + if (sig_interrupt_fds[2] == 1) { + return; + } + socketpair(AF_UNIX, SOCK_STREAM, 0, sig_interrupt_fds.data()); + sig_interrupt_fds[2] = 1; + + struct sigaction sa; + sa.sa_handler = &GMainWindow::HandleSigInterrupt; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sa, nullptr); + sigaction(SIGTERM, &sa, nullptr); + + sig_interrupt_notifier = new QSocketNotifier(sig_interrupt_fds[1], QSocketNotifier::Read, this); + connect(sig_interrupt_notifier, &QSocketNotifier::activated, this, + &GMainWindow::OnSigInterruptNotifierActivated); + connect(this, &GMainWindow::SigInterrupt, this, &GMainWindow::close); +} + +void GMainWindow::HandleSigInterrupt(int sig) { + if (sig == SIGINT) { + exit(1); + } + + // Calling into Qt directly from a signal handler is not safe, + // so wake up a QSocketNotifier with this hacky write call instead. + char a = 1; + write(sig_interrupt_fds[0], &a, sizeof(a)); +} + +void GMainWindow::OnSigInterruptNotifierActivated() { + sig_interrupt_notifier->setEnabled(false); + + char a; + read(sig_interrupt_fds[1], &a, sizeof(a)); + + sig_interrupt_notifier->setEnabled(true); + + emit SigInterrupt(); +} #endif // __linux__ void GMainWindow::PreventOSSleep() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8cf224c9c2..c55fd2a02d 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -161,6 +161,8 @@ signals: void WebBrowserExtractOfflineRomFS(); void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); + void SigInterrupt(); + public slots: void OnLoadComplete(); void OnExecuteProgram(std::size_t program_index); @@ -246,6 +248,12 @@ private: void RequestGameExit(); void closeEvent(QCloseEvent* event) override; +#ifdef __linux__ + void SetupSigInterrupts(); + static void HandleSigInterrupt(int); + void OnSigInterruptNotifierActivated(); +#endif + private slots: void OnStartGame(); void OnRestartGame(); @@ -414,6 +422,9 @@ private: bool is_tas_recording_dialog_active{}; #ifdef __linux__ + QSocketNotifier* sig_interrupt_notifier; + static std::array sig_interrupt_fds; + QDBusObjectPath wake_lock{}; #endif