This repository has been archived on 2025-12-24. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
yachat/iris-legacy/iris/irisnet/processquit.cpp

223 lines
4 KiB
C++
Raw Permalink Normal View History

2025-12-25 01:37:49 +05:00
/*
* Copyright (C) 2006 Justin Karneges
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "processquit.h"
#ifndef NO_IRISNET
# include "irisnetglobal_p.h"
#endif
#ifdef QT_GUI_LIB
# include <QApplication>
#endif
#ifdef Q_OS_WIN
# include <windows.h>
#endif
#ifdef Q_OS_UNIX
# include <signal.h>
# include <unistd.h>
#endif
#ifndef NO_IRISNET
namespace XMPP {
#endif
Q_GLOBAL_STATIC(QMutex, pq_mutex)
static ProcessQuit *g_pq = 0;
inline bool is_gui_app()
{
#ifdef QT_GUI_LIB
return (QApplication::type() != QApplication::Tty);
#else
return false;
#endif
}
class ProcessQuit::Private : public QObject
{
Q_OBJECT
public:
ProcessQuit *q;
bool done;
#ifdef Q_OS_WIN
bool use_handler;
#endif
#ifdef Q_OS_UNIX
int sig_pipe[2];
QSocketNotifier *sig_notifier;
#endif
Private(ProcessQuit *_q) : QObject(_q), q(_q)
{
done = false;
#ifdef Q_OS_WIN
use_handler = !is_gui_app();
if(use_handler)
SetConsoleCtrlHandler((PHANDLER_ROUTINE)winHandler, TRUE);
#endif
#ifdef Q_OS_UNIX
pipe(sig_pipe);
sig_notifier = new QSocketNotifier(sig_pipe[0], QSocketNotifier::Read, this);
connect(sig_notifier, SIGNAL(activated(int)), SLOT(sig_activated(int)));
unixWatchAdd(SIGINT);
unixWatchAdd(SIGHUP);
unixWatchAdd(SIGTERM);
#endif
}
~Private()
{
#ifdef Q_OS_WIN
if(use_handler)
SetConsoleCtrlHandler((PHANDLER_ROUTINE)winHandler, FALSE);
#endif
#ifdef Q_OS_UNIX
unixWatchRemove(SIGINT);
unixWatchRemove(SIGHUP);
unixWatchRemove(SIGTERM);
delete sig_notifier;
close(sig_pipe[0]);
close(sig_pipe[1]);
#endif
}
#ifdef Q_OS_WIN
static BOOL winHandler(DWORD ctrlType)
{
Q_UNUSED(ctrlType);
QMetaObject::invokeMethod(g_pq->d, "ctrl_ready", Qt::QueuedConnection);
return TRUE;
}
#endif
#ifdef Q_OS_UNIX
static void unixHandler(int sig)
{
Q_UNUSED(sig);
unsigned char c = 0;
::write(g_pq->d->sig_pipe[1], &c, 1);
}
void unixWatchAdd(int sig)
{
struct sigaction sa;
sigaction(sig, NULL, &sa);
// if the signal is ignored, don't take it over. this is
// recommended by the glibc manual
if(sa.sa_handler == SIG_IGN)
return;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
sa.sa_handler = unixHandler;
sigaction(sig, &sa, 0);
}
void unixWatchRemove(int sig)
{
struct sigaction sa;
sigaction(sig, NULL, &sa);
// ignored means we skipped it earlier, so we should
// skip it again
if(sa.sa_handler == SIG_IGN)
return;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
sa.sa_handler = SIG_DFL;
sigaction(sig, &sa, 0);
}
#endif
public slots:
void ctrl_ready()
{
#ifdef Q_OS_WIN
do_emit();
#endif
}
void sig_activated(int)
{
#ifdef Q_OS_UNIX
unsigned char c;
::read(sig_pipe[0], &c, 1);
do_emit();
#endif
}
private:
void do_emit()
{
// only signal once
if(!done)
{
done = true;
emit q->quit();
}
}
};
ProcessQuit::ProcessQuit(QObject *parent)
:QObject(parent)
{
d = new Private(this);
}
ProcessQuit::~ProcessQuit()
{
delete d;
}
ProcessQuit *ProcessQuit::instance()
{
QMutexLocker locker(pq_mutex());
if(!g_pq)
{
g_pq = new ProcessQuit;
g_pq->moveToThread(QCoreApplication::instance()->thread());
#ifndef NO_IRISNET
irisNetAddPostRoutine(cleanup);
#endif
}
return g_pq;
}
void ProcessQuit::reset()
{
QMutexLocker locker(pq_mutex());
if(g_pq)
g_pq->d->done = false;
}
void ProcessQuit::cleanup()
{
delete g_pq;
g_pq = 0;
}
#ifndef NO_IRISNET
}
#endif
#include "processquit.moc"