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