1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/generic/timer.cpp
3// Purpose:     wxTimer implementation
4// Author:      Vaclav Slavik
5// Id:          $Id: timer.cpp 40943 2006-08-31 19:31:43Z ABX $
6// Copyright:   (c) Vaclav Slavik
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#ifdef __BORLANDC__
14    #pragma hdrstop
15#endif
16
17// ----------------------------------------------------------------------------
18// NB: when using generic wxTimer implementation in your port, you *must* call
19//     wxTimer::NotifyTimers() often enough. The ideal place for this
20//     is in wxEventLoop::Dispatch().
21// ----------------------------------------------------------------------------
22
23#if wxUSE_TIMER
24
25#include "wx/timer.h"
26
27#ifndef WX_PRECOMP
28    #include "wx/log.h"
29    #include "wx/module.h"
30#endif
31
32// ----------------------------------------------------------------------------
33// Time input function
34// ----------------------------------------------------------------------------
35
36#ifdef __WXMGL__
37    // We take advantage of wxMGL's _EVT_getTicks because it is faster
38    // (especially under MS-DOS!) and more precise than wxGetLocalTimeMillis
39    // if we are unlucky and the latter combines information from two sources.
40    #include "wx/mgl/private.h"
41    extern "C" ulong _EVT_getTicks();
42    #define GetMillisecondsTime _EVT_getTicks
43
44    typedef ulong wxTimerTick_t;
45
46    #define wxTimerTickFmtSpec _T("lu")
47    #define wxTimerTickPrintfArg(tt) (tt)
48
49    #ifdef __DOS__
50        // Under DOS the MGL timer has a 24hr period, so consider the 12 hours
51        // before y to be 'less' and the the 12 hours after 'greater' modulo
52        // 24 hours.
53        inline bool wxTickGreaterEqual(wxTimerTick_t x, wxTimerTick_t y)
54        {
55            // _EVT_getTicks wraps at 1573040 * 55
56            const wxTimerTick_t modulus = 1573040 * 55;
57            return (2 * modulus + x - y) % modulus < modulus / 2;
58        }
59    #else
60        // If wxTimerTick_t is 32-bits then it'll wrap in around 50 days. So
61        // let the 25 days before y be 'less' and 25 days after be 'greater'.
62        inline bool wxTickGreaterEqual(wxTimerTick_t x, wxTimerTick_t y)
63        {
64            // This code assumes wxTimerTick_t is an unsigned type.
65            // Set half_modulus with top bit set and the rest zeros.
66            const wxTimerTick_t half_modulus = ~((~(wxTimerTick_t)0) >> 1);
67            return x - y < half_modulus;
68        }
69    #endif
70#else // !__WXMGL__
71    #define GetMillisecondsTime wxGetLocalTimeMillis
72
73    typedef wxLongLong wxTimerTick_t;
74
75    #if wxUSE_LONGLONG_WX
76        #define wxTimerTickFmtSpec wxLongLongFmtSpec _T("d")
77        #define wxTimerTickPrintfArg(tt) (tt.GetValue())
78    #else // using native wxLongLong
79        #define wxTimerTickFmtSpec _T("s")
80        #define wxTimerTickPrintfArg(tt) (tt.ToString().c_str())
81    #endif // wx/native long long
82
83    inline bool wxTickGreaterEqual(wxTimerTick_t x, wxTimerTick_t y)
84    {
85        return x >= y;
86    }
87#endif // __WXMGL__/!__WXMGL__
88
89// ----------------------------------------------------------------------------
90// helper structures and wxTimerScheduler
91// ----------------------------------------------------------------------------
92
93class wxTimerDesc
94{
95public:
96    wxTimerDesc(wxTimer *t) :
97        timer(t), running(false), next(NULL), prev(NULL),
98        shotTime(0), deleteFlag(NULL) {}
99
100    wxTimer         *timer;
101    bool             running;
102    wxTimerDesc     *next, *prev;
103    wxTimerTick_t    shotTime;
104    volatile bool   *deleteFlag; // see comment in ~wxTimer
105};
106
107class wxTimerScheduler
108{
109public:
110    wxTimerScheduler() : m_timers(NULL) {}
111
112    void QueueTimer(wxTimerDesc *desc, wxTimerTick_t when = 0);
113    void RemoveTimer(wxTimerDesc *desc);
114    void NotifyTimers();
115
116private:
117    wxTimerDesc *m_timers;
118};
119
120void wxTimerScheduler::QueueTimer(wxTimerDesc *desc, wxTimerTick_t when)
121{
122    if ( desc->running )
123        return; // already scheduled
124
125    if ( when == 0 )
126        when = GetMillisecondsTime() + desc->timer->GetInterval();
127    desc->shotTime = when;
128    desc->running = true;
129
130    wxLogTrace( wxT("timer"),
131                wxT("queued timer %p at tick %") wxTimerTickFmtSpec,
132               desc->timer,  wxTimerTickPrintfArg(when));
133
134    if ( m_timers )
135    {
136        wxTimerDesc *d = m_timers;
137        while ( d->next && d->next->shotTime < when ) d = d->next;
138        desc->next = d->next;
139        desc->prev = d;
140        if ( d->next )
141            d->next->prev = desc;
142        d->next = desc;
143    }
144    else
145    {
146        m_timers = desc;
147        desc->prev = desc->next = NULL;
148    }
149}
150
151void wxTimerScheduler::RemoveTimer(wxTimerDesc *desc)
152{
153    desc->running = false;
154    if ( desc == m_timers )
155        m_timers = desc->next;
156    if ( desc->prev )
157        desc->prev->next = desc->next;
158    if ( desc->next )
159        desc->next->prev = desc->prev;
160    desc->prev = desc->next = NULL;
161}
162
163void wxTimerScheduler::NotifyTimers()
164{
165    if ( m_timers )
166    {
167        bool oneShot;
168        volatile bool timerDeleted;
169        wxTimerTick_t now = GetMillisecondsTime();
170
171        for ( wxTimerDesc *desc = m_timers; desc; desc = desc->next )
172        {
173            if ( desc->running && wxTickGreaterEqual(now, desc->shotTime) )
174            {
175                oneShot = desc->timer->IsOneShot();
176                RemoveTimer(desc);
177
178                timerDeleted = false;
179                desc->deleteFlag = &timerDeleted;
180                desc->timer->Notify();
181
182                if ( !timerDeleted )
183                {
184                    wxLogTrace( wxT("timer"),
185                                wxT("notified timer %p sheduled for %")
186                                wxTimerTickFmtSpec,
187                                desc->timer,
188                                wxTimerTickPrintfArg(desc->shotTime) );
189
190                    desc->deleteFlag = NULL;
191                    if ( !oneShot )
192                        QueueTimer(desc, now + desc->timer->GetInterval());
193                }
194                else
195                {
196                    desc = m_timers;
197                    if (!desc)
198                        break;
199                }
200            }
201        }
202    }
203}
204
205
206// ----------------------------------------------------------------------------
207// wxTimer
208// ----------------------------------------------------------------------------
209
210IMPLEMENT_ABSTRACT_CLASS(wxTimer, wxEvtHandler)
211
212wxTimerScheduler *gs_scheduler = NULL;
213
214void wxTimer::Init()
215{
216    if ( !gs_scheduler )
217        gs_scheduler = new wxTimerScheduler;
218    m_desc = new wxTimerDesc(this);
219}
220
221wxTimer::~wxTimer()
222{
223    wxLogTrace( wxT("timer"), wxT("destroying timer %p..."), this);
224    if ( IsRunning() )
225        Stop();
226
227    // NB: this is a hack: wxTimerScheduler must have some way of knowing
228    //     that wxTimer object was deleted under its hands -- this may
229    //     happen if somebody is really nasty and deletes the timer
230    //     from wxTimer::Notify()
231    if ( m_desc->deleteFlag != NULL )
232        *m_desc->deleteFlag = true;
233
234    delete m_desc;
235    wxLogTrace( wxT("timer"), wxT("    ...done destroying timer %p..."), this);
236}
237
238bool wxTimer::IsRunning() const
239{
240    return m_desc->running;
241}
242
243bool wxTimer::Start(int millisecs, bool oneShot)
244{
245    wxLogTrace( wxT("timer"), wxT("started timer %p: %i ms, oneshot=%i"),
246               this, millisecs, oneShot);
247
248    if ( !wxTimerBase::Start(millisecs, oneShot) )
249        return false;
250
251    gs_scheduler->QueueTimer(m_desc);
252    return true;
253}
254
255void wxTimer::Stop()
256{
257    if ( !m_desc->running ) return;
258
259    gs_scheduler->RemoveTimer(m_desc);
260}
261
262/*static*/ void wxTimer::NotifyTimers()
263{
264    if ( gs_scheduler )
265        gs_scheduler->NotifyTimers();
266}
267
268
269
270// A module to deallocate memory properly:
271class wxTimerModule: public wxModule
272{
273DECLARE_DYNAMIC_CLASS(wxTimerModule)
274public:
275    wxTimerModule() {}
276    bool OnInit() { return true; }
277    void OnExit() { delete gs_scheduler; gs_scheduler = NULL; }
278};
279
280IMPLEMENT_DYNAMIC_CLASS(wxTimerModule, wxModule)
281
282
283#endif //wxUSE_TIMER
284