initial import
[vuplus_webkit] / Source / WebCore / platform / win / SharedTimerWin.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, 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 COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "SharedTimer.h"
28
29 #include "Page.h"
30 #include "Settings.h"
31 #include "WebCoreInstanceHandle.h"
32 #include "Widget.h"
33 #include <wtf/Assertions.h>
34 #include <wtf/CurrentTime.h>
35
36 // Note: wx headers set defines that affect the configuration of windows.h
37 // so we must include the wx header first to get unicode versions of functions,
38 // etc.
39 #if PLATFORM(WX)
40 #include <wx/wx.h>
41 #endif
42
43 #include <windows.h>
44 #include <mmsystem.h>
45
46 #if PLATFORM(WIN)
47 #include "PluginView.h"
48 #endif
49
50 // These aren't in winuser.h with the MSVS 2003 Platform SDK, 
51 // so use default values in that case.
52 #ifndef USER_TIMER_MINIMUM
53 #define USER_TIMER_MINIMUM 0x0000000A
54 #endif
55
56 #ifndef USER_TIMER_MAXIMUM
57 #define USER_TIMER_MAXIMUM 0x7FFFFFFF
58 #endif
59
60 #ifndef QS_RAWINPUT
61 #define QS_RAWINPUT         0x0400
62 #endif
63
64 namespace WebCore {
65
66 static UINT timerID;
67 static void (*sharedTimerFiredFunction)();
68
69 static HWND timerWindowHandle = 0;
70 static UINT timerFiredMessage = 0;
71 static HANDLE timerQueue;
72 static HANDLE timer;
73 static bool highResTimerActive;
74 static bool processingCustomTimerMessage = false;
75 static LONG pendingTimers;
76
77 const LPCWSTR kTimerWindowClassName = L"TimerWindowClass";
78 const int timerResolution = 1; // To improve timer resolution, we call timeBeginPeriod/timeEndPeriod with this value to increase timer resolution to 1ms.
79 const int highResolutionThresholdMsec = 16; // Only activate high-res timer for sub-16ms timers (Windows can fire timers at 16ms intervals without changing the system resolution).
80 const int stopHighResTimerInMsec = 300; // Stop high-res timer after 0.3 seconds to lessen power consumption (we don't use a smaller time since oscillating between high and low resolution breaks timer accuracy on XP).
81
82 enum {
83     sharedTimerID = 1000,
84     endHighResTimerID = 1001,
85 };
86
87 LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
88 {
89 #if PLATFORM(WIN)
90     // Windows Media Player has a modal message loop that will deliver messages
91     // to us at inappropriate times and we will crash if we handle them when
92     // they are delivered. We repost all messages so that we will get to handle
93     // them once the modal loop exits.
94     if (PluginView::isCallingPlugin()) {
95         PostMessage(hWnd, message, wParam, lParam);
96         return 0;
97     }
98 #endif
99
100     if (message == timerFiredMessage) {
101         InterlockedExchange(&pendingTimers, 0);
102         processingCustomTimerMessage = true;
103         sharedTimerFiredFunction();
104         processingCustomTimerMessage = false;
105     } else if (message == WM_TIMER) {
106         if (wParam == sharedTimerID) {
107             KillTimer(timerWindowHandle, sharedTimerID);
108             sharedTimerFiredFunction();
109         } else if (wParam == endHighResTimerID) {
110             KillTimer(timerWindowHandle, endHighResTimerID);
111             highResTimerActive = false;
112             timeEndPeriod(timerResolution);
113         }
114     } else
115         return DefWindowProc(hWnd, message, wParam, lParam);
116
117     return 0;
118 }
119
120 static void initializeOffScreenTimerWindow()
121 {
122     if (timerWindowHandle)
123         return;
124     
125     WNDCLASSEX wcex;
126     memset(&wcex, 0, sizeof(WNDCLASSEX));
127     wcex.cbSize = sizeof(WNDCLASSEX);
128     wcex.lpfnWndProc    = TimerWindowWndProc;
129     wcex.hInstance      = WebCore::instanceHandle();
130     wcex.lpszClassName  = kTimerWindowClassName;
131     RegisterClassEx(&wcex);
132
133     timerWindowHandle = CreateWindow(kTimerWindowClassName, 0, 0,
134        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, 0, WebCore::instanceHandle(), 0);
135     timerFiredMessage = RegisterWindowMessage(L"com.apple.WebKit.TimerFired");
136 }
137
138 void setSharedTimerFiredFunction(void (*f)())
139 {
140     sharedTimerFiredFunction = f;
141 }
142
143 static void NTAPI queueTimerProc(PVOID, BOOLEAN)
144 {
145     if (InterlockedIncrement(&pendingTimers) == 1)
146         PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
147 }
148
149 void setSharedTimerFireInterval(double interval)
150 {
151     ASSERT(sharedTimerFiredFunction);
152
153     unsigned intervalInMS;
154     interval *= 1000;
155     if (interval > USER_TIMER_MAXIMUM)
156         intervalInMS = USER_TIMER_MAXIMUM;
157     else
158         intervalInMS = static_cast<unsigned>(interval);
159
160     initializeOffScreenTimerWindow();
161     bool timerSet = false;
162
163     if (Settings::shouldUseHighResolutionTimers()) {
164         if (interval < highResolutionThresholdMsec) {
165             if (!highResTimerActive) {
166                 highResTimerActive = true;
167                 timeBeginPeriod(timerResolution);
168             }
169             SetTimer(timerWindowHandle, endHighResTimerID, stopHighResTimerInMsec, 0);
170         }
171
172         DWORD queueStatus = LOWORD(GetQueueStatus(QS_PAINT | QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT));
173
174         // Win32 has a tri-level queue with application messages > user input > WM_PAINT/WM_TIMER.
175
176         // If the queue doesn't contains input events, we use a higher priorty timer event posting mechanism.
177         if (!(queueStatus & (QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT))) {
178             if (intervalInMS < USER_TIMER_MINIMUM && !processingCustomTimerMessage && !(queueStatus & QS_PAINT)) {
179                 // Call PostMessage immediately if the timer is already expired, unless a paint is pending.
180                 // (we prioritize paints over timers)
181                 if (InterlockedIncrement(&pendingTimers) == 1)
182                     PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
183                 timerSet = true;
184             } else {
185                 // Otherwise, delay the PostMessage via a CreateTimerQueueTimer
186                 if (!timerQueue)
187                     timerQueue = CreateTimerQueue();
188                 if (timer)
189                     DeleteTimerQueueTimer(timerQueue, timer, 0);
190                 timerSet = CreateTimerQueueTimer(&timer, timerQueue, queueTimerProc, 0, intervalInMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
191             }
192         }
193     }
194
195     if (timerSet) {
196         if (timerID) {
197             KillTimer(timerWindowHandle, timerID);
198             timerID = 0;
199         }
200     } else {
201         timerID = SetTimer(timerWindowHandle, sharedTimerID, intervalInMS, 0);
202         timer = 0;
203     }
204 }
205
206 void stopSharedTimer()
207 {
208     if (timerQueue && timer) {
209         DeleteTimerQueueTimer(timerQueue, timer, 0);
210         timer = 0;
211     }
212
213     if (timerID) {
214         KillTimer(timerWindowHandle, timerID);
215         timerID = 0;
216     }
217 }
218
219 }