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