1 | // Aseprite UI Library |
---|---|
2 | // Copyright (C) 2018-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2017 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "ui/timer.h" |
13 | |
14 | #include "base/time.h" |
15 | #include "ui/manager.h" |
16 | #include "ui/message.h" |
17 | #include "ui/system.h" |
18 | #include "ui/widget.h" |
19 | |
20 | #include <algorithm> |
21 | #include <limits> |
22 | #include <vector> |
23 | |
24 | namespace ui { |
25 | |
26 | typedef std::vector<Timer*> Timers; |
27 | |
28 | static Timers timers; // Registered timers |
29 | static int running_timers = 0; |
30 | |
31 | Timer::Timer(int interval, Widget* owner) |
32 | : m_owner(owner ? owner: Manager::getDefault()) |
33 | , m_interval(interval) |
34 | , m_running(false) |
35 | , m_lastTick(0) |
36 | { |
37 | ASSERT(m_owner != nullptr); |
38 | assert_ui_thread(); |
39 | |
40 | timers.push_back(this); |
41 | } |
42 | |
43 | Timer::~Timer() |
44 | { |
45 | assert_ui_thread(); |
46 | |
47 | auto it = std::find(timers.begin(), timers.end(), this); |
48 | ASSERT(it != timers.end()); |
49 | if (it != timers.end()) |
50 | timers.erase(it); |
51 | |
52 | // Stop the timer and remove it from the message queue. |
53 | stop(); |
54 | } |
55 | |
56 | void Timer::start() |
57 | { |
58 | assert_ui_thread(); |
59 | |
60 | // Infinite timer? Do nothing. |
61 | if (m_interval == 0) |
62 | return; |
63 | |
64 | m_lastTick = base::current_tick(); |
65 | if (!m_running) { |
66 | m_running = true; |
67 | ++running_timers; |
68 | } |
69 | } |
70 | |
71 | void Timer::stop() |
72 | { |
73 | assert_ui_thread(); |
74 | |
75 | if (m_running) { |
76 | m_running = false; |
77 | --running_timers; |
78 | |
79 | // Remove messages of this timer in the queue. The expected behavior |
80 | // is that when we stop a timer, we'll not receive more messages |
81 | // about it (even if there are enqueued messages waiting in the |
82 | // message queue). |
83 | Manager::getDefault()->removeMessagesForTimer(this); |
84 | } |
85 | } |
86 | |
87 | void Timer::tick() |
88 | { |
89 | assert_ui_thread(); |
90 | |
91 | onTick(); |
92 | } |
93 | |
94 | void Timer::setInterval(int interval) |
95 | { |
96 | m_interval = interval; |
97 | } |
98 | |
99 | void Timer::onTick() |
100 | { |
101 | // Fire Tick signal. |
102 | Tick(); |
103 | } |
104 | |
105 | void Timer::pollTimers() |
106 | { |
107 | assert_ui_thread(); |
108 | |
109 | // Generate messages for timers |
110 | if (running_timers != 0) { |
111 | ASSERT(!timers.empty()); |
112 | base::tick_t t = base::current_tick(); |
113 | |
114 | for (auto timer : timers) { |
115 | if (timer && timer->isRunning()) { |
116 | ASSERT(timer->interval() > 0); |
117 | |
118 | int64_t count = ((t - timer->m_lastTick) / timer->m_interval); |
119 | if (count > 0) { |
120 | timer->m_lastTick += count * timer->m_interval; |
121 | |
122 | ASSERT(timer->m_owner != nullptr); |
123 | |
124 | Message* msg = new TimerMessage(count, timer); |
125 | msg->setRecipient(timer->m_owner); |
126 | Manager::getDefault()->enqueueMessage(msg); |
127 | } |
128 | } |
129 | } |
130 | } |
131 | } |
132 | |
133 | bool Timer::haveTimers() |
134 | { |
135 | return !timers.empty(); |
136 | } |
137 | |
138 | bool Timer::getNextTimeout(double& timeout) |
139 | { |
140 | if (running_timers == 0) |
141 | return false; |
142 | |
143 | base::tick_t t = base::current_tick(); |
144 | bool result = false; |
145 | timeout = std::numeric_limits<double>::max(); |
146 | for (auto timer : timers) { |
147 | if (timer && timer->isRunning()) { |
148 | int64_t diff = (timer->m_lastTick + timer->m_interval) - t; |
149 | if (diff < 0) { |
150 | timeout = 0.0; // Right-now |
151 | return true; |
152 | } |
153 | else { |
154 | timeout = std::min<double>(timeout, diff / 1000.0); |
155 | result = true; |
156 | } |
157 | } |
158 | } |
159 | return result; |
160 | } |
161 | |
162 | } // namespace ui |
163 |