Home | History | Annotate | Download | only in win
      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