2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "SharedTimer.h"
31 #include "WebCoreInstanceHandle.h"
33 #include <wtf/Assertions.h>
34 #include <wtf/CurrentTime.h>
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,
47 #include "PluginView.h"
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
56 #ifndef USER_TIMER_MAXIMUM
57 #define USER_TIMER_MAXIMUM 0x7FFFFFFF
61 #define QS_RAWINPUT 0x0400
67 static void (*sharedTimerFiredFunction)();
69 static HWND timerWindowHandle = 0;
70 static UINT timerFiredMessage = 0;
71 static HANDLE timerQueue;
73 static bool highResTimerActive;
74 static bool processingCustomTimerMessage = false;
75 static LONG pendingTimers;
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).
84 endHighResTimerID = 1001,
87 LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
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);
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);
115 return DefWindowProc(hWnd, message, wParam, lParam);
120 static void initializeOffScreenTimerWindow()
122 if (timerWindowHandle)
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);
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");
138 void setSharedTimerFiredFunction(void (*f)())
140 sharedTimerFiredFunction = f;
143 static void NTAPI queueTimerProc(PVOID, BOOLEAN)
145 if (InterlockedIncrement(&pendingTimers) == 1)
146 PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
149 void setSharedTimerFireInterval(double interval)
151 ASSERT(sharedTimerFiredFunction);
153 unsigned intervalInMS;
155 if (interval > USER_TIMER_MAXIMUM)
156 intervalInMS = USER_TIMER_MAXIMUM;
158 intervalInMS = static_cast<unsigned>(interval);
160 initializeOffScreenTimerWindow();
161 bool timerSet = false;
163 if (Settings::shouldUseHighResolutionTimers()) {
164 if (interval < highResolutionThresholdMsec) {
165 if (!highResTimerActive) {
166 highResTimerActive = true;
167 timeBeginPeriod(timerResolution);
169 SetTimer(timerWindowHandle, endHighResTimerID, stopHighResTimerInMsec, 0);
172 DWORD queueStatus = LOWORD(GetQueueStatus(QS_PAINT | QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT));
174 // Win32 has a tri-level queue with application messages > user input > WM_PAINT/WM_TIMER.
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);
185 // Otherwise, delay the PostMessage via a CreateTimerQueueTimer
187 timerQueue = CreateTimerQueue();
189 DeleteTimerQueueTimer(timerQueue, timer, 0);
190 timerSet = CreateTimerQueueTimer(&timer, timerQueue, queueTimerProc, 0, intervalInMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
197 KillTimer(timerWindowHandle, timerID);
201 timerID = SetTimer(timerWindowHandle, sharedTimerID, intervalInMS, 0);
206 void stopSharedTimer()
208 if (timerQueue && timer) {
209 DeleteTimerQueueTimer(timerQueue, timer, 0);
214 KillTimer(timerWindowHandle, timerID);