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 "config.h"
27#include "RunLoop.h"
28
29#include "WindowsExtras.h"
30#include <wtf/CurrentTime.h>
31
32using namespace std;
33
34namespace WebCore {
35
36static const UINT PerformWorkMessage = WM_USER + 1;
37static const LPWSTR kRunLoopMessageWindowClassName = L"RunLoopMessageWindow";
38
39LRESULT CALLBACK RunLoop::RunLoopWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
40{
41    if (RunLoop* runLoop = static_cast<RunLoop*>(getWindowPointer(hWnd, 0)))
42        return runLoop->wndProc(hWnd, message, wParam, lParam);
43
44    if (message == WM_CREATE) {
45        LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
46
47        // Associate the RunLoop with the window.
48        setWindowPointer(hWnd, 0, createStruct->lpCreateParams);
49        return 0;
50    }
51
52    return ::DefWindowProc(hWnd, message, wParam, lParam);
53}
54
55LRESULT RunLoop::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
56{
57    switch (message) {
58    case PerformWorkMessage:
59        performWork();
60        return 0;
61    case WM_TIMER:
62        RunLoop::TimerBase::timerFired(this, wParam);
63        return 0;
64    }
65
66    return ::DefWindowProc(hWnd, message, wParam, lParam);
67}
68
69void RunLoop::run()
70{
71    MSG message;
72    while (BOOL result = ::GetMessage(&message, 0, 0, 0)) {
73        if (result == -1)
74            break;
75        ::TranslateMessage(&message);
76        ::DispatchMessage(&message);
77    }
78}
79
80void RunLoop::stop()
81{
82    ::PostQuitMessage(0);
83}
84
85bool RunLoop::registerRunLoopMessageWindowClass()
86{
87    // FIXME: This really only needs to be called once.
88
89    WNDCLASS windowClass = { 0 };
90    windowClass.lpfnWndProc     = RunLoop::RunLoopWndProc;
91    windowClass.cbWndExtra      = sizeof(RunLoop*);
92    windowClass.lpszClassName   = kRunLoopMessageWindowClassName;
93
94    return !!::RegisterClass(&windowClass);
95}
96
97RunLoop::RunLoop()
98{
99    registerRunLoopMessageWindowClass();
100
101    m_runLoopMessageWindow = ::CreateWindow(kRunLoopMessageWindowClassName, 0, 0,
102                                            CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
103                                            HWND_MESSAGE, 0, 0, this);
104    ASSERT(::IsWindow(m_runLoopMessageWindow));
105}
106
107RunLoop::~RunLoop()
108{
109    // FIXME: Tear down the work item queue here.
110}
111
112void RunLoop::wakeUp()
113{
114    // FIXME: No need to wake up the run loop if we've already called dispatch
115    // before the run loop has had the time to respond.
116    ::PostMessage(m_runLoopMessageWindow, PerformWorkMessage, reinterpret_cast<WPARAM>(this), 0);
117}
118
119// RunLoop::Timer
120
121void RunLoop::TimerBase::timerFired(RunLoop* runLoop, uint64_t ID)
122{
123    TimerMap::iterator it = runLoop->m_activeTimers.find(ID);
124    if (it == runLoop->m_activeTimers.end()) {
125        // The timer must have been stopped after the WM_TIMER message was posted to the message queue.
126        return;
127    }
128
129    TimerBase* timer = it->value;
130
131    if (!timer->m_isRepeating) {
132        runLoop->m_activeTimers.remove(it);
133        ::KillTimer(runLoop->m_runLoopMessageWindow, ID);
134    }
135
136    timer->fired();
137}
138
139static uint64_t generateTimerID()
140{
141    static uint64_t uniqueTimerID = 1;
142    return uniqueTimerID++;
143}
144
145RunLoop::TimerBase::TimerBase(RunLoop* runLoop)
146    : m_runLoop(runLoop)
147    , m_ID(generateTimerID())
148    , m_isRepeating(false)
149{
150}
151
152RunLoop::TimerBase::~TimerBase()
153{
154    stop();
155}
156
157void RunLoop::TimerBase::start(double nextFireInterval, bool repeat)
158{
159    m_isRepeating = repeat;
160    m_runLoop->m_activeTimers.set(m_ID, this);
161    ::SetTimer(m_runLoop->m_runLoopMessageWindow, m_ID, nextFireInterval * 1000, 0);
162}
163
164void RunLoop::TimerBase::stop()
165{
166    TimerMap::iterator it = m_runLoop->m_activeTimers.find(m_ID);
167    if (it == m_runLoop->m_activeTimers.end())
168        return;
169
170    m_runLoop->m_activeTimers.remove(it);
171    ::KillTimer(m_runLoop->m_runLoopMessageWindow, m_ID);
172}
173
174bool RunLoop::TimerBase::isActive() const
175{
176    return m_runLoop->m_activeTimers.contains(m_ID);
177}
178
179} // namespace WebCore
180