myxlib/src/myx/qt/posix_signal_watcher.cpp

131 lines
3.7 KiB
C++
Raw Normal View History

2020-06-27 07:04:56 +00:00
#ifndef MYX_QT_POSIX_SIGNAL_WATCHER_CPP_
#define MYX_QT_POSIX_SIGNAL_WATCHER_CPP_
#include <myx/core/config.hpp>
#include <myx/backports/cpp/helpers.hpp>
2020-06-27 07:04:56 +00:00
#include <myx/qt/posix_signal_watcher.hpp>
#include <myx/qt/posix_signal_watcher_p.hpp>
2020-06-27 07:04:56 +00:00
namespace myx {
namespace qt {
2020-06-29 19:24:15 +00:00
std::array< int, 2 > PosixSignalWatcherPrivate::mSockpair { { 0, 0 } };
2020-06-27 07:04:56 +00:00
PosixSignalWatcherPrivate::~PosixSignalWatcherPrivate() = default;
PosixSignalWatcherPrivate::PosixSignalWatcherPrivate( PosixSignalWatcher* q ) :
q_ptr( q )
{
#if MYX_QT_HAS_POSIX_SIGNALS
// Create socket pair
if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, mSockpair.data() ) != 0 )
2020-06-27 07:04:56 +00:00
{
qDebug() << "PosixSignalWatcher: socketpair: " << ::strerror( errno );
return;
}
#endif
// Create a notifier for the read end of the pair
// m_notifier.reset( new QSocketNotifier( mSockpair[1], QSocketNotifier::Read ) );
m_notifier = std::make_unique< QSocketNotifier >( mSockpair[1], QSocketNotifier::Read );
2020-06-27 07:04:56 +00:00
// Called when the signal handler has written to the socket pair.
// Emits the Posix signal as a Qt signal.
connect( m_notifier.get(), &QSocketNotifier::activated, q, [this]( int sockfd ) {
Q_Q( PosixSignalWatcher );
int signal = 0;
(void)::read( sockfd, &signal, sizeof( signal ) );
qDebug() << "Caught signal: " << ::strsignal( signal );
Q_EMIT q->posixSignal( signal );
} );
m_notifier->setEnabled( true );
}
/*!
* Registers a handler for the given Posix \a signal. The handler will write to
* a socket pair, the other end of which is connected to a QSocketNotifier.
* This provides a way to break out of the asynchronous context from which the
* signal handler is called and back into the Qt event loop.
*/
MYXLIB_INLINE void PosixSignalWatcherPrivate::watchForSignal( int signal )
2020-06-27 07:04:56 +00:00
{
if ( m_watchedSignals.contains( signal ) )
{
qDebug() << "Already watching for signal " << signal;
return;
}
#if MYX_QT_HAS_POSIX_SIGNALS
// Register a sigaction which will write to the socket pair
struct sigaction sigact {};
sigact.sa_handler = PosixSignalWatcherPrivate::signalHandler; //NOLINT
2020-06-27 07:04:56 +00:00
sigact.sa_flags = 0;
sigemptyset( &sigact.sa_mask );
sigact.sa_flags |= SA_RESTART;
if ( ::sigaction( signal, &sigact, nullptr ) != 0 )
2020-06-27 07:04:56 +00:00
{
qDebug() << "PosixSignalWatcher: sigaction: " << ::strerror( errno );
return;
}
#endif
m_watchedSignals.append( signal );
} // PosixSignalWatcherPrivate::watchForSignal
/*!
* Called when a Posix \a signal is received. Write to the socket to wake up the
* QSocketNotifier.
*/
MYXLIB_INLINE void PosixSignalWatcherPrivate::signalHandler( int signal )
{
(void)::write( mSockpair[0], &signal, sizeof( signal ) );
2020-06-27 07:04:56 +00:00
}
/*!
* Create a new PosixSignalWatcher as a child of the given \a parent.
*/
MYXLIB_INLINE PosixSignalWatcher::PosixSignalWatcher( QObject* parent ) :
QObject( parent ),
d_ptr ( new PosixSignalWatcherPrivate( this ) )
{
}
/*!
* Register a signal handler for the given \a signal.
*
* After calling this method you can \c connect() to the POSIXSignal() Qt signal
* to be notified when the Posix signal is received.
*/
MYXLIB_INLINE void PosixSignalWatcher::watchForSignal( int signal )
{
Q_D( PosixSignalWatcher );
d->watchForSignal( signal );
}
/*!
* \fn void PosixSignalWatcher::posixSignal(int signal)
* Emitted when the given Posix \a signal is received.
*
* watchForSignal() must be called for each Posix signal that you want to receive
* via the POSIXSignal() Qt signal. If a watcher is watching multiple signals,
* POSIXSignal() will be emitted whenever *any* of the watched Posix signals are
* received, and the \a signal argument can be inspected to find out which one
* was actually received.
*/
} // namespace qt
} // namespace myx
#endif // ifndef MYX_QT_POSIX_SIGNAL_WATCHER_CPP_