1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 module dfl.timer;
5 
6 import core.sys.windows.windows;
7 import dfl.exception;
8 import dfl.event;
9 import dfl.base;
10 import dfl.application;
11 import dfl.internal.dlib;
12 
13 debug (APP_PRINT) {
14    private import dfl.internal.clib;
15 }
16 
17 class Timer {
18    //EventHandler tick;
19    Event!(Timer, EventArgs) tick;
20 
21    @property void enabled(bool on) {
22       if (on) {
23          start();
24       } else {
25          stop();
26       }
27    }
28 
29    @property bool enabled() {
30       return timerId != 0;
31    }
32 
33    final @property void interval(size_t timeout) {
34       if (!timeout) {
35          throw new DflException("Invalid timer interval");
36       }
37 
38       if (this._timeout != timeout) {
39          this._timeout = timeout;
40 
41          if (timerId) {
42             // I don't know if this is the correct behavior.
43             // Reset the timer for the new timeout...
44             stop();
45             start();
46          }
47       }
48    }
49 
50    final @property size_t interval() {
51       return _timeout;
52    }
53 
54    final void start() {
55       if (timerId) {
56          return;
57       }
58 
59       assert(_timeout > 0);
60 
61       timerId = SetTimer(null, 0, _timeout, &timerProc);
62       if (!timerId) {
63          throw new DflException("Unable to start timer");
64       }
65       allTimers[timerId] = this;
66    }
67 
68    final void stop() {
69       if (timerId) {
70          //delete allTimers[timerId];
71          allTimers.remove(timerId);
72          KillTimer(null, timerId);
73          timerId = 0;
74       }
75    }
76 
77    this() {
78    }
79 
80    this(void delegate(Timer) dg) {
81       this();
82       if (dg) {
83          this._dg = dg;
84          tick ~= &_dgcall;
85       }
86    }
87 
88    this(void delegate(Object, EventArgs) dg) {
89       assert(dg !is null);
90 
91       this();
92       tick ~= dg;
93    }
94 
95    this(void delegate(Timer, EventArgs) dg) {
96       assert(dg !is null);
97 
98       this();
99       tick ~= dg;
100    }
101 
102    ~this() {
103       dispose();
104    }
105 
106 protected:
107 
108    void dispose() {
109       stop();
110    }
111 
112    void onTick(EventArgs ea) {
113       tick(this, ea);
114    }
115 
116 private:
117    DWORD _timeout = 100;
118    UINT timerId = 0;
119    void delegate(Timer) _dg;
120 
121    void _dgcall(Object sender, EventArgs ea) {
122       assert(_dg !is null);
123       _dg(this);
124    }
125 }
126 
127 private:
128 
129 Timer[UINT] allTimers;
130 
131 extern (Windows) void timerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) nothrow {
132    try {
133       if (idEvent in allTimers) {
134          allTimers[idEvent].onTick(EventArgs.empty);
135       } else {
136          debug (APP_PRINT)
137             cprintf("Unknown timer 0x%X.\n", idEvent);
138       }
139    }
140    catch (DThrowable e) {
141       Application.onThreadException(e);
142    }
143 }