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 "Widget.h" 32 #include <wtf/Assertions.h> 33 #include <wtf/CurrentTime.h> 34 35 // Note: wx headers set defines that affect the configuration of windows.h 36 // so we must include the wx header first to get unicode versions of functions, 37 // etc. 38 #if PLATFORM(WX) 39 #include <wx/wx.h> 40 #endif 41 42 #include <windows.h> 43 #include <mmsystem.h> 44 45 #if PLATFORM(WIN) 46 #include "PluginView.h" 47 #endif 48 49 // These aren't in winuser.h with the MSVS 2003 Platform SDK, 50 // so use default values in that case. 51 #ifndef USER_TIMER_MINIMUM 52 #define USER_TIMER_MINIMUM 0x0000000A 53 #endif 54 55 #ifndef USER_TIMER_MAXIMUM 56 #define USER_TIMER_MAXIMUM 0x7FFFFFFF 57 #endif 58 59 #ifndef QS_RAWINPUT 60 #define QS_RAWINPUT 0x0400 61 #endif 62 63 namespace WebCore { 64 65 static UINT timerID; 66 static void (*sharedTimerFiredFunction)(); 67 68 static HWND timerWindowHandle = 0; 69 static UINT timerFiredMessage = 0; 70 static HANDLE timerQueue; 71 static HANDLE timer; 72 static bool highResTimerActive; 73 static bool processingCustomTimerMessage = false; 74 static LONG pendingTimers; 75 76 const LPCWSTR kTimerWindowClassName = L"TimerWindowClass"; 77 const int timerResolution = 1; // To improve timer resolution, we call timeBeginPeriod/timeEndPeriod with this value to increase timer resolution to 1ms. 78 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). 79 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). 80 81 enum { 82 sharedTimerID = 1000, 83 endHighResTimerID = 1001, 84 }; 85 86 LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 87 { 88 #if PLATFORM(WIN) 89 // Windows Media Player has a modal message loop that will deliver messages 90 // to us at inappropriate times and we will crash if we handle them when 91 // they are delivered. We repost all messages so that we will get to handle 92 // them once the modal loop exits. 93 if (PluginView::isCallingPlugin()) { 94 PostMessage(hWnd, message, wParam, lParam); 95 return 0; 96 } 97 #endif 98 99 if (message == timerFiredMessage) { 100 InterlockedExchange(&pendingTimers, 0); 101 processingCustomTimerMessage = true; 102 sharedTimerFiredFunction(); 103 processingCustomTimerMessage = false; 104 } else if (message == WM_TIMER) { 105 if (wParam == sharedTimerID) { 106 KillTimer(timerWindowHandle, sharedTimerID); 107 sharedTimerFiredFunction(); 108 } else if (wParam == endHighResTimerID) { 109 KillTimer(timerWindowHandle, endHighResTimerID); 110 highResTimerActive = false; 111 timeEndPeriod(timerResolution); 112 } 113 } else 114 return DefWindowProc(hWnd, message, wParam, lParam); 115 116 return 0; 117 } 118 119 static void initializeOffScreenTimerWindow() 120 { 121 if (timerWindowHandle) 122 return; 123 124 WNDCLASSEX wcex; 125 memset(&wcex, 0, sizeof(WNDCLASSEX)); 126 wcex.cbSize = sizeof(WNDCLASSEX); 127 wcex.lpfnWndProc = TimerWindowWndProc; 128 wcex.hInstance = Page::instanceHandle(); 129 wcex.lpszClassName = kTimerWindowClassName; 130 RegisterClassEx(&wcex); 131 132 timerWindowHandle = CreateWindow(kTimerWindowClassName, 0, 0, 133 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, 0, Page::instanceHandle(), 0); 134 timerFiredMessage = RegisterWindowMessage(L"com.apple.WebKit.TimerFired"); 135 } 136 137 void setSharedTimerFiredFunction(void (*f)()) 138 { 139 sharedTimerFiredFunction = f; 140 } 141 142 static void NTAPI queueTimerProc(PVOID, BOOLEAN) 143 { 144 if (InterlockedIncrement(&pendingTimers) == 1) 145 PostMessage(timerWindowHandle, timerFiredMessage, 0, 0); 146 } 147 148 void setSharedTimerFireTime(double fireTime) 149 { 150 ASSERT(sharedTimerFiredFunction); 151 152 double interval = fireTime - currentTime(); 153 unsigned intervalInMS; 154 if (interval < 0) 155 intervalInMS = 0; 156 else { 157 interval *= 1000; 158 if (interval > USER_TIMER_MAXIMUM) 159 intervalInMS = USER_TIMER_MAXIMUM; 160 else 161 intervalInMS = (unsigned)interval; 162 } 163 164 initializeOffScreenTimerWindow(); 165 bool timerSet = false; 166 167 if (Settings::shouldUseHighResolutionTimers()) { 168 if (interval < highResolutionThresholdMsec) { 169 if (!highResTimerActive) { 170 highResTimerActive = true; 171 timeBeginPeriod(timerResolution); 172 } 173 SetTimer(timerWindowHandle, endHighResTimerID, stopHighResTimerInMsec, 0); 174 } 175 176 DWORD queueStatus = LOWORD(GetQueueStatus(QS_PAINT | QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT)); 177 178 // Win32 has a tri-level queue with application messages > user input > WM_PAINT/WM_TIMER. 179 180 // If the queue doesn't contains input events, we use a higher priorty timer event posting mechanism. 181 if (!(queueStatus & (QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT))) { 182 if (intervalInMS < USER_TIMER_MINIMUM && !processingCustomTimerMessage && !(queueStatus & QS_PAINT)) { 183 // Call PostMessage immediately if the timer is already expired, unless a paint is pending. 184 // (we prioritize paints over timers) 185 if (InterlockedIncrement(&pendingTimers) == 1) 186 PostMessage(timerWindowHandle, timerFiredMessage, 0, 0); 187 timerSet = true; 188 } else { 189 // Otherwise, delay the PostMessage via a CreateTimerQueueTimer 190 if (!timerQueue) 191 timerQueue = CreateTimerQueue(); 192 if (timer) 193 DeleteTimerQueueTimer(timerQueue, timer, 0); 194 timerSet = CreateTimerQueueTimer(&timer, timerQueue, queueTimerProc, 0, intervalInMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE); 195 } 196 } 197 } 198 199 if (timerSet) { 200 if (timerID) { 201 KillTimer(timerWindowHandle, timerID); 202 timerID = 0; 203 } 204 } else { 205 timerID = SetTimer(timerWindowHandle, sharedTimerID, intervalInMS, 0); 206 timer = 0; 207 } 208 } 209 210 void stopSharedTimer() 211 { 212 if (timerQueue && timer) { 213 DeleteTimerQueueTimer(timerQueue, timer, 0); 214 timer = 0; 215 } 216 217 if (timerID) { 218 KillTimer(timerWindowHandle, timerID); 219 timerID = 0; 220 } 221 } 222 223 } 224