-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This fixes a usability problem, when blobdrop sets the keep-below hint, but then is forcefully terminated by the user pressing ^C, which means that the keep-below hint is never lifted. Fix this by catching terminating Unix signals and then performing a last-second cleanup before actually quitting. Catching Unix signals is trivial [0] if we use only async-signal-safe methods [1]. Howver we acually need to do some Qt function calls, that would definitely violate these restrictions, so instead we use a more elaborate setup [2], where we perform some safe writes via Unix sockets during the signal interception, which will later trigger a Qt signal on a listening QSocketNotifier when we are out of the critical zone again. When the QSocketNotifier::activated() triggers, we are then safe to do any operations we like. [0] https://gist.github.com/azadkuh/a2ac6869661ebd3f8588 [1] https://pubs.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_03 [2] https://doc.qt.io/qt-6/unix-signals.html
- Loading branch information
Showing
3 changed files
with
81 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#include "signals.hpp" | ||
|
||
#include <ranges> | ||
#include <signal.h> | ||
#include <sys/socket.h> | ||
|
||
#include "backend.hpp" | ||
|
||
Signals::Signals(const std::initializer_list<int> &sigs) { | ||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fd)) { | ||
qWarning() << "Could not create HUP socketpair"; | ||
} | ||
sn = new QSocketNotifier(signal_fd[1], QSocketNotifier::Read, this); | ||
connect(sn, &QSocketNotifier::activated, this, &Signals::handle_qt_signal); | ||
setup_signal_handlers(sigs); | ||
} | ||
|
||
void Signals::handle_unix_signal(int) { | ||
char a = 1; | ||
// only very few, async-signal-safe methods are allowed in the signal handler | ||
write(signal_fd[0], &a, sizeof(a)); | ||
} | ||
|
||
void Signals::handle_qt_signal() { | ||
sn->setEnabled(false); | ||
char a; | ||
read(signal_fd[1], &a, sizeof(a)); | ||
|
||
// remove the keep-below hint again, as we want to quit now | ||
Backend::get()->restore_terminal(); | ||
// Duplicate quit attempts are required: | ||
// The nice attempt over quit_delayed() is ignored, because a drag operation is active. | ||
// The exit() forces the drag operation to close. | ||
// Then the quit_delayed() causes the program to finally close. | ||
QCoreApplication::exit(); | ||
Backend::get()->quit_delayed(0ms); | ||
|
||
sn->setEnabled(true); | ||
} | ||
|
||
void Signals::setup_signal_handlers(const std::initializer_list<int> &sigs) { | ||
struct sigaction act; | ||
|
||
act.sa_handler = Signals::handle_unix_signal; | ||
sigemptyset(&act.sa_mask); | ||
act.sa_flags = 0; | ||
act.sa_flags |= SA_RESTART; | ||
|
||
std::ranges::for_each(sigs, [&](const auto &s) { | ||
if (sigaction(SIGINT, &act, nullptr)) { | ||
qWarning() << "Could not setup signal handler"; | ||
return; | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#pragma once | ||
|
||
#include <QSocketNotifier> | ||
|
||
class Signals : public QObject { | ||
Q_OBJECT | ||
public: | ||
Signals(const std::initializer_list<int> &sigs); | ||
|
||
// Unix signal handler | ||
static void handle_unix_signal(int); | ||
public slots: | ||
// Qt signal handler | ||
void handle_qt_signal(); | ||
private: | ||
void setup_signal_handlers(const std::initializer_list<int> &sigs); | ||
|
||
static inline int signal_fd[2]; | ||
QSocketNotifier *sn; | ||
}; |