4
0
mirror of https://github.com/QuasarApp/qca.git synced 2025-05-13 02:59:34 +00:00
qca/src/qca_safetimer.cpp
Albert Astals Cid 7df8f7e215 Move to the new connect syntax
QProcess::errorOccurred is since 5.6 so increase min version
2020-01-29 08:34:13 +00:00

247 lines
5.6 KiB
C++

/*
* qca_safetimer.cpp - Qt Cryptographic Architecture
* Copyright (C) 2014 Ivan Romanov <drizt@land.ru>
*
* 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 "qca_safetimer.h"
#include <QElapsedTimer>
#include <qmath.h>
#include <QTimerEvent>
// #define SAFETIMER_DEBUG
#ifdef SAFETIMER_DEBUG
#include <QDebug>
#endif
namespace QCA
{
class SafeTimer::Private : public QObject
{
Q_OBJECT
friend class SafeTimer;
public:
Private(QObject *parent = nullptr);
int timerId;
int fixerTimerId;
bool isSingleShot;
int interval;
bool isActive;
QElapsedTimer elapsedTimer;
public Q_SLOTS:
void fixTimer();
Q_SIGNALS:
void needFix();
protected:
bool event(QEvent *event) override;
void timerEvent(QTimerEvent *event) override;
};
SafeTimer::Private::Private(QObject *parent)
: QObject(parent)
, timerId(0)
, fixerTimerId(0)
, isSingleShot(false)
, interval(0)
, isActive(false)
, elapsedTimer(QElapsedTimer())
{
connect(this, &Private::needFix, this, &Private::fixTimer, Qt::QueuedConnection);
}
void SafeTimer::Private::fixTimer()
{
// Start special timer to align ressurected old timer
int msec = qMax(0, interval - static_cast<int>(elapsedTimer.elapsed()));
fixerTimerId = startTimer(msec);
#ifdef SAFETIMER_DEBUG
qDebug() << "START FIXTIMER: id =" << fixerTimerId << ", thread =" << thread() << ", interval =" << msec << parent();
#endif
}
bool SafeTimer::Private::event(QEvent *event)
{
if (event->type() == QEvent::ThreadChange && fixerTimerId /* timer is actived */)
{
// Timer dies when an object changes owner thread. This trick
// used to ressurect old timer in the new thread.
// Signal is emited in the old thread but will be gotten in the new one.
#ifdef SAFETIMER_DEBUG
qDebug() << "STOP FIXTIMER ON CHANGE THREAD: id =" << fixerTimerId << ", thread =" << thread() << parent();
#endif
killTimer(fixerTimerId);
fixerTimerId = 0;
emit needFix();
}
return QObject::event(event);
}
void SafeTimer::Private::timerEvent(QTimerEvent *event)
{
if (event->timerId() == fixerTimerId)
{
#ifdef SAFETIMER_DEBUG
qDebug() << "STOP FIXTIMER ON TIMEOUT: id =" << fixerTimerId << ", thread =" << thread() << parent();
#endif
killTimer(fixerTimerId);
fixerTimerId = 0;
SafeTimer *safeTimer = qobject_cast<SafeTimer*>(parent());
// Emulate timeout signal of not yet ressurected timer
emit safeTimer->timeout();
// Ressurect timer here if not a singleshot
if (!isSingleShot)
safeTimer->start();
else
isActive = false;
}
else
{
#ifdef SAFETIMER_DEBUG
qDebug() << "BAD PRIVATE TIME EVENT: id =" << timerId << ", thread =" << thread() << this << ", badId =" << event->timerId() << parent();
#endif
}
}
SafeTimer::SafeTimer(QObject *parent)
: QObject()
, d(new Private())
{
// It must be done here. Initialization list can't be used.
// Need to have proper class name. Look at TimerFixer::hook.
setParent(parent);
d->setParent(this);
}
SafeTimer::~SafeTimer()
{
}
int SafeTimer::interval() const
{
return d->interval;
}
bool SafeTimer::isActive() const
{
return d->isActive;
}
bool SafeTimer::isSingleShot() const
{
return d->isSingleShot;
}
void SafeTimer::setInterval(int msec)
{
d->interval = msec;
}
void SafeTimer::setSingleShot(bool singleShot)
{
d->isSingleShot = singleShot;
}
void SafeTimer::start(int msec)
{
d->interval = msec;
start();
}
void SafeTimer::start()
{
stop();
d->elapsedTimer.start();
d->timerId = QObject::startTimer(d->interval);
d->isActive = d->timerId > 0;
#ifdef SAFETIMER_DEBUG
qDebug() << "START TIMER: id =" << d->timerId << ", thread =" << thread() << ", interval =" << d->interval << this;
#endif
}
void SafeTimer::stop()
{
if (d->timerId)
{
QObject::killTimer(d->timerId);
#ifdef SAFETIMER_DEBUG
qDebug() << "STOP TIMER: id =" << d->timerId << ", thread =" << thread() << this;
#endif
d->timerId = 0;
}
if (d->fixerTimerId)
{
#ifdef SAFETIMER_DEBUG
qDebug() << "STOP FIXER TIMER: id =" << d->fixerTimerId << ", thread =" << thread() << this;
#endif
d->killTimer(d->fixerTimerId);
d->fixerTimerId = 0;
}
d->isActive = false;
}
bool SafeTimer::event(QEvent *event)
{
if (event->type() == QEvent::ThreadChange && d->timerId /* timer is actived */)
{
// Timer dies when an object changes owner thread. This trick
// used to ressurect old timer in the new thread.
// Signal is emited in the old thread but will be gotten in the new one.
#ifdef SAFETIMER_DEBUG
qDebug() << "CHANGE THREAD: id =" << d->timerId << ", thread =" << thread() << this;
#endif
killTimer(d->timerId);
d->timerId = 0;
emit d->needFix();
}
return QObject::event(event);
}
void SafeTimer::timerEvent(QTimerEvent *event)
{
if (event->timerId() == d->timerId)
{
if (d->isSingleShot)
stop();
emit timeout();
}
else
{
#ifdef SAFETIMER_DEBUG
qDebug() << "BAD TIME EVENT: id =" << d->timerId << ", thread =" << thread() << this << ", badId =" << event->timerId() << this;
#endif
}
}
} // end namespace QCA
#include "qca_safetimer.moc"