|
1 /* |
|
2 * Copyright (C) 2010 Apple Inc. All rights reserved. |
|
3 * |
|
4 * Redistribution and use in source and binary forms, with or without |
|
5 * modification, are permitted provided that the following conditions |
|
6 * are met: |
|
7 * 1. Redistributions of source code must retain the above copyright |
|
8 * notice, this list of conditions and the following disclaimer. |
|
9 * 2. Redistributions in binary form must reproduce the above copyright |
|
10 * notice, this list of conditions and the following disclaimer in the |
|
11 * documentation and/or other materials provided with the distribution. |
|
12 * |
|
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
|
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
|
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
|
23 * THE POSSIBILITY OF SUCH DAMAGE. |
|
24 */ |
|
25 |
|
26 #include "RunLoop.h" |
|
27 |
|
28 #include "WorkItem.h" |
|
29 |
|
30 static const UINT PerformWorkMessage = WM_USER + 1; |
|
31 static const LPWSTR kRunLoopMessageWindowClassName = L"RunLoopMessageWindow"; |
|
32 |
|
33 LRESULT CALLBACK RunLoop::RunLoopWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) |
|
34 { |
|
35 LONG_PTR longPtr = ::GetWindowLongPtr(hWnd, 0); |
|
36 |
|
37 if (RunLoop* runLoop = reinterpret_cast<RunLoop*>(longPtr)) |
|
38 return runLoop->wndProc(hWnd, message, wParam, lParam); |
|
39 |
|
40 if (message == WM_CREATE) { |
|
41 LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam); |
|
42 |
|
43 // Associate the RunLoop with the window. |
|
44 ::SetWindowLongPtr(hWnd, 0, (LONG_PTR)createStruct->lpCreateParams); |
|
45 return 0; |
|
46 } |
|
47 |
|
48 return ::DefWindowProc(hWnd, message, wParam, lParam); |
|
49 } |
|
50 |
|
51 LRESULT RunLoop::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) |
|
52 { |
|
53 switch (message) { |
|
54 case PerformWorkMessage: |
|
55 performWork(); |
|
56 return 0; |
|
57 case WM_TIMER: |
|
58 RunLoop::TimerBase::timerFired(this, wParam); |
|
59 return 0; |
|
60 } |
|
61 |
|
62 return ::DefWindowProc(hWnd, message, wParam, lParam); |
|
63 } |
|
64 |
|
65 void RunLoop::run() |
|
66 { |
|
67 MSG message; |
|
68 while (BOOL result = ::GetMessage(&message, 0, 0, 0)) { |
|
69 if (result == -1) |
|
70 break; |
|
71 ::TranslateMessage(&message); |
|
72 ::DispatchMessage(&message); |
|
73 } |
|
74 } |
|
75 |
|
76 void RunLoop::stop() |
|
77 { |
|
78 ::PostQuitMessage(0); |
|
79 } |
|
80 |
|
81 bool RunLoop::registerRunLoopMessageWindowClass() |
|
82 { |
|
83 // FIXME: This really only needs to be called once. |
|
84 |
|
85 WNDCLASSEX windowClass = { 0 }; |
|
86 windowClass.cbSize = sizeof(windowClass); |
|
87 windowClass.lpfnWndProc = RunLoop::RunLoopWndProc; |
|
88 windowClass.cbWndExtra = sizeof(RunLoop*); |
|
89 windowClass.lpszClassName = kRunLoopMessageWindowClassName; |
|
90 |
|
91 return !!::RegisterClassEx(&windowClass); |
|
92 } |
|
93 |
|
94 RunLoop::RunLoop() |
|
95 { |
|
96 registerRunLoopMessageWindowClass(); |
|
97 |
|
98 m_runLoopMessageWindow = ::CreateWindow(kRunLoopMessageWindowClassName, 0, 0, |
|
99 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, |
|
100 HWND_MESSAGE, 0, 0, this); |
|
101 ASSERT(::IsWindow(m_runLoopMessageWindow)); |
|
102 } |
|
103 |
|
104 RunLoop::~RunLoop() |
|
105 { |
|
106 // FIXME: Tear down the work item queue here. |
|
107 } |
|
108 |
|
109 void RunLoop::wakeUp() |
|
110 { |
|
111 // FIXME: No need to wake up the run loop if we've already called scheduleWork |
|
112 // before the run loop has had the time to respond. |
|
113 ::PostMessage(m_runLoopMessageWindow, PerformWorkMessage, reinterpret_cast<WPARAM>(this), 0); |
|
114 } |
|
115 |
|
116 // RunLoop::Timer |
|
117 |
|
118 void RunLoop::TimerBase::timerFired(RunLoop* runLoop, uint64_t ID) |
|
119 { |
|
120 TimerMap::iterator it = runLoop->m_activeTimers.find(ID); |
|
121 ASSERT(it != runLoop->m_activeTimers.end()); |
|
122 TimerBase* timer = it->second; |
|
123 |
|
124 timer->fired(); |
|
125 } |
|
126 |
|
127 static uint64_t generateTimerID() |
|
128 { |
|
129 static uint64_t uniqueTimerID = 1; |
|
130 return uniqueTimerID++; |
|
131 } |
|
132 |
|
133 RunLoop::TimerBase::TimerBase(RunLoop* runLoop) |
|
134 : m_runLoop(runLoop) |
|
135 , m_ID(generateTimerID()) |
|
136 { |
|
137 } |
|
138 |
|
139 RunLoop::TimerBase::~TimerBase() |
|
140 { |
|
141 stop(); |
|
142 } |
|
143 |
|
144 void RunLoop::TimerBase::start(double nextFireInterval, double /*repeatInterval*/) |
|
145 { |
|
146 // FIMXE: Support repeating timers. |
|
147 |
|
148 m_runLoop->m_activeTimers.set(m_ID, this); |
|
149 ::SetTimer(m_runLoop->m_runLoopMessageWindow, m_ID, nextFireInterval, 0); |
|
150 } |
|
151 |
|
152 void RunLoop::TimerBase::stop() |
|
153 { |
|
154 TimerMap::iterator it = m_runLoop->m_activeTimers.find(m_ID); |
|
155 if (it == m_runLoop->m_activeTimers.end()) |
|
156 return; |
|
157 |
|
158 m_runLoop->m_activeTimers.remove(it); |
|
159 ::KillTimer(m_runLoop->m_runLoopMessageWindow, m_ID); |
|
160 } |
|
161 |
|
162 bool RunLoop::TimerBase::isActive() const |
|
163 { |
|
164 return m_runLoop->m_activeTimers.contains(m_ID); |
|
165 } |