#ifndef MYX_QT_POSIX_SIGNAL_WATCHER_CPP_ #define MYX_QT_POSIX_SIGNAL_WATCHER_CPP_ #include #include #include namespace myx { namespace qt { std::array< int, 2 > PosixSignalWatcherPrivate::mSockpair { { 0, 0 } }; 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 ) { 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 ); // 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 ) { 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 sigact.sa_flags = 0; sigemptyset( &sigact.sa_mask ); sigact.sa_flags |= SA_RESTART; if ( ::sigaction( signal, &sigact, nullptr ) != 0 ) { 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 ) ); } /*! * 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_