The source files for this module are listed below. You can also download the module C_Timer as a zip archive; this archive contains all the source files and documentation.
CTimer allows you to receive events on regular intervals without handling the Windows timer messages yourself.
This source code also uses our signal/slot library and our CSubclassedWindow class. You can also download these at our website.
To start using a timer, you first have to initialize it. Since the timer uses the standard Windows timer messages, the timer needs to have a window to route the messages through. This is done by subclassing the window. Since each timer has it's own ID behind the scenes, you can attach as many timers to a window as desired. You can re-initialize a timer to a different window in order to use that window and stop using the previous one.
Once you have initialized the timer, you can call it's Start, Pauze, Resume and Stop methods in order to start, pauze, resume and stop the timer events. Calling Start two times in a row will restart the timer on the second call with the given new interval. Calling Pauze and Resume will only work if the timer was started/resumed or pauzed, respectively. The current state of the timer can be retrieved by calling the methods IsRunning, IsPauzed and State.
When you stop the timer, you can also let the timer generate a last timer event so the event sequence is terminated. This timer event is however fired immediately.
The event that is raised has no return value. It is only meant for informative purposes.
Each file belonging to this source code module is listed below.
/*******************************************************************************
Version: 5
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#ifndef INCLUDE_TWOLOGS_COMMON_TIMER_H
#define INCLUDE_TWOLOGS_COMMON_TIMER_H
#include "../C_SubclassedWindow/SubclassedWindow.h"
#include "../C_SignalSlot/Signal.h"
class TTimer;
// A timer's state
enum class TTimerState {
stopped,
pauzed,
running
};
// Notifies when the timer went off
struct TTimerWentOffEvent: public TEventTypeNoResult {
TTimer* timerPtr;
};
class TTimer:
private TSubclassedWindow,
public TSignal<TTimerWentOffEvent> {
public:
// Con- & destructor
TTimer();
virtual ~TTimer();
// (Re-)initializes the timer to use the given window
void Initialize(HWND window);
// Starts or restarts the timer
void Start(UINT millisecondInterval);
// Stops the timer
void Stop(bool raiseTimerEvent);
// Pauses/resumes the timer
void Pauze();
void Resume();
// Whether the timer is running
bool IsRunning() const;
// Whether the timer is pauzed
bool IsPauzed() const;
// The timer's state
TTimerState State() const;
private:
// The last used ID
static DWORD m_lastUsedID;
// Our ID
DWORD m_id;
// The window's handle
HWND m_window;
// The timer state
TTimerState m_state;
// The interval set (in millisec)
UINT m_interval;
// The time the timer was last started
DWORD m_lastStartedTime;
// The remaining number of milliseconds to run, if pauzed
UINT m_remainingInterval;
// Whether to re-adjust the physical timer to the intended interval
bool m_reAdjustTimer;
// Raises the timer event
void RaiseTimerEvent();
// Processes the given message
void ProcessMessage(TMessage& msg);
};
#endif // INCLUDE_TWOLOGS_COMMON_TIMER_H
/*******************************************************************************
Version: 5
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#include "Timer.h"
// The last used ID
DWORD TTimer::m_lastUsedID = 0;
// Con- & destructor
TTimer::TTimer():
m_id(m_lastUsedID++),
m_window(NULL),
m_state(TTimerState::stopped),
m_reAdjustTimer(false) {
}
TTimer::~TTimer() {
Stop(false);
}
// (Re-)initializes the timer to use the given window
void TTimer::Initialize(HWND window) {
// Look if to detach from a previous window
if (m_window != NULL) {
// Yes -> do so
StopSubclassing(m_window);
}
// Note the window to use
m_window = window;
// And start subclassing it, if needed
if (m_window != NULL) {
Subclass(m_window);
}
}
// Starts or restarts the timer
void TTimer::Start(UINT millisecondInterval) {
if (m_window != NULL) {
m_interval = millisecondInterval;
m_lastStartedTime = GetTickCount();
SetTimer(m_window, m_id, millisecondInterval, nullptr);
m_state = TTimerState::running;
}
}
// Stops the timer
void TTimer::Stop(bool raiseTimerEvent) {
// Kill the timer
if (m_window != NULL) {
KillTimer(m_window, m_id);
m_state = TTimerState::stopped;
}
// And look if to raise a final timer event
if (raiseTimerEvent) {
// Yes -> do so
RaiseTimerEvent();
}
}
// Pauses/resumes the timer
void TTimer::Pauze() {
// Look if we're running at all
if (m_state == TTimerState::running) {
// Yes -> pauze the timer
if (m_window != NULL) {
KillTimer(m_window, m_id);
m_state = TTimerState::pauzed;
DWORD now = GetTickCount();
DWORD ranInterval = now - m_lastStartedTime;
m_remainingInterval = m_interval - ranInterval;
}
}
}
void TTimer::Resume() {
// Look if we're pauzed
if (m_state == TTimerState::pauzed) {
// Yes -> restart the timer where we left off
if (m_window != NULL) {
DWORD now = GetTickCount();
UINT usedInterval = m_interval - m_remainingInterval;
m_lastStartedTime = now - usedInterval;
// ...fake an earlier start to match the desired interval
m_reAdjustTimer = true;
SetTimer(m_window, m_id, m_remainingInterval, nullptr);
m_state = TTimerState::running;
}
}
}
// Whether the timer is running
bool TTimer::IsRunning() const {
return m_state != TTimerState::stopped;
}
// Whether the timer is pauzed
bool TTimer::IsPauzed() const {
return m_state == TTimerState::pauzed;
}
// The timer's state
TTimerState TTimer::State() const {
return m_state;
}
// Raises the timer event
void TTimer::RaiseTimerEvent() {
TTimerWentOffEvent event;
event.timerPtr = this;
TSignal<TTimerWentOffEvent>::RaiseEvent(event);
}
// Processes the given message
void TTimer::ProcessMessage(TMessage& msg) {
// Look if it's a timer message
msg.stop = false;
if (msg.code == WM_TIMER) {
// Yes -> look if it's our timer
if (msg.wParam == m_id) {
// Yes -> re-adjust the timer, if needed
if (m_reAdjustTimer && m_state == TTimerState::running) {
KillTimer(m_window, m_id);
m_lastStartedTime = GetTickCount();
SetTimer(m_window, m_id, m_interval, nullptr);
m_reAdjustTimer = false;
}
// And raise the timer event
msg.stop = true;
RaiseTimerEvent();
}
}
}