163 lines
3.2 KiB
C++
163 lines
3.2 KiB
C++
// Aseprite UI Library
|
|
// Copyright (C) 2018-2022 Igara Studio S.A.
|
|
// Copyright (C) 2001-2017 David Capello
|
|
//
|
|
// This file is released under the terms of the MIT license.
|
|
// Read LICENSE.txt for more information.
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "ui/timer.h"
|
|
|
|
#include "base/time.h"
|
|
#include "ui/manager.h"
|
|
#include "ui/message.h"
|
|
#include "ui/system.h"
|
|
#include "ui/widget.h"
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <vector>
|
|
|
|
namespace ui {
|
|
|
|
typedef std::vector<Timer*> Timers;
|
|
|
|
static Timers timers; // Registered timers
|
|
static int running_timers = 0;
|
|
|
|
Timer::Timer(int interval, Widget* owner)
|
|
: m_owner(owner ? owner: Manager::getDefault())
|
|
, m_interval(interval)
|
|
, m_running(false)
|
|
, m_lastTick(0)
|
|
{
|
|
ASSERT(m_owner != nullptr);
|
|
assert_ui_thread();
|
|
|
|
timers.push_back(this);
|
|
}
|
|
|
|
Timer::~Timer()
|
|
{
|
|
assert_ui_thread();
|
|
|
|
auto it = std::find(timers.begin(), timers.end(), this);
|
|
ASSERT(it != timers.end());
|
|
if (it != timers.end())
|
|
timers.erase(it);
|
|
|
|
// Stop the timer and remove it from the message queue.
|
|
stop();
|
|
}
|
|
|
|
void Timer::start()
|
|
{
|
|
assert_ui_thread();
|
|
|
|
// Infinite timer? Do nothing.
|
|
if (m_interval == 0)
|
|
return;
|
|
|
|
m_lastTick = base::current_tick();
|
|
if (!m_running) {
|
|
m_running = true;
|
|
++running_timers;
|
|
}
|
|
}
|
|
|
|
void Timer::stop()
|
|
{
|
|
assert_ui_thread();
|
|
|
|
if (m_running) {
|
|
m_running = false;
|
|
--running_timers;
|
|
|
|
// Remove messages of this timer in the queue. The expected behavior
|
|
// is that when we stop a timer, we'll not receive more messages
|
|
// about it (even if there are enqueued messages waiting in the
|
|
// message queue).
|
|
Manager::getDefault()->removeMessagesForTimer(this);
|
|
}
|
|
}
|
|
|
|
void Timer::tick()
|
|
{
|
|
assert_ui_thread();
|
|
|
|
onTick();
|
|
}
|
|
|
|
void Timer::setInterval(int interval)
|
|
{
|
|
m_interval = interval;
|
|
}
|
|
|
|
void Timer::onTick()
|
|
{
|
|
// Fire Tick signal.
|
|
Tick();
|
|
}
|
|
|
|
void Timer::pollTimers()
|
|
{
|
|
assert_ui_thread();
|
|
|
|
// Generate messages for timers
|
|
if (running_timers != 0) {
|
|
ASSERT(!timers.empty());
|
|
base::tick_t t = base::current_tick();
|
|
|
|
for (auto timer : timers) {
|
|
if (timer && timer->isRunning()) {
|
|
ASSERT(timer->interval() > 0);
|
|
|
|
int64_t count = ((t - timer->m_lastTick) / timer->m_interval);
|
|
if (count > 0) {
|
|
timer->m_lastTick += count * timer->m_interval;
|
|
|
|
ASSERT(timer->m_owner != nullptr);
|
|
|
|
Message* msg = new TimerMessage(count, timer);
|
|
msg->setRecipient(timer->m_owner);
|
|
Manager::getDefault()->enqueueMessage(msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Timer::haveTimers()
|
|
{
|
|
return !timers.empty();
|
|
}
|
|
|
|
bool Timer::getNextTimeout(double& timeout)
|
|
{
|
|
if (running_timers == 0)
|
|
return false;
|
|
|
|
base::tick_t t = base::current_tick();
|
|
bool result = false;
|
|
timeout = std::numeric_limits<double>::max();
|
|
for (auto timer : timers) {
|
|
if (timer && timer->isRunning()) {
|
|
int64_t diff = (timer->m_lastTick + timer->m_interval) - t;
|
|
if (diff < 0) {
|
|
timeout = 0.0; // Right-now
|
|
return true;
|
|
}
|
|
else {
|
|
timeout = std::min<double>(timeout, diff / 1000.0);
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace ui
|