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 "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 setSharedTimerFireTime(double fireTime)
    150 {
    151     ASSERT(sharedTimerFiredFunction);
    152 
    153     double interval = fireTime - currentTime();
    154     unsigned intervalInMS;
    155     if (interval < 0)
    156         intervalInMS = 0;
    157     else {
    158         interval *= 1000;
    159         if (interval > USER_TIMER_MAXIMUM)
    160             intervalInMS = USER_TIMER_MAXIMUM;
    161         else
    162             intervalInMS = (unsigned)interval;
    163     }
    164 
    165     initializeOffScreenTimerWindow();
    166     bool timerSet = false;
    167 
    168     if (Settings::shouldUseHighResolutionTimers()) {
    169         if (interval < highResolutionThresholdMsec) {
    170             if (!highResTimerActive) {
    171                 highResTimerActive = true;
    172                 timeBeginPeriod(timerResolution);
    173             }
    174             SetTimer(timerWindowHandle, endHighResTimerID, stopHighResTimerInMsec, 0);
    175         }
    176 
    177         DWORD queueStatus = LOWORD(GetQueueStatus(QS_PAINT | QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT));
    178 
    179         // Win32 has a tri-level queue with application messages > user input > WM_PAINT/WM_TIMER.
    180 
    181         // If the queue doesn't contains input events, we use a higher priorty timer event posting mechanism.
    182         if (!(queueStatus & (QS_MOUSEBUTTON | QS_KEY | QS_RAWINPUT))) {
    183             if (intervalInMS < USER_TIMER_MINIMUM && !processingCustomTimerMessage && !(queueStatus & QS_PAINT)) {
    184                 // Call PostMessage immediately if the timer is already expired, unless a paint is pending.
    185                 // (we prioritize paints over timers)
    186                 if (InterlockedIncrement(&pendingTimers) == 1)
    187                     PostMessage(timerWindowHandle, timerFiredMessage, 0, 0);
    188                 timerSet = true;
    189             } else {
    190                 // Otherwise, delay the PostMessage via a CreateTimerQueueTimer
    191                 if (!timerQueue)
    192                     timerQueue = CreateTimerQueue();
    193                 if (timer)
    194                     DeleteTimerQueueTimer(timerQueue, timer, 0);
    195                 timerSet = CreateTimerQueueTimer(&timer, timerQueue, queueTimerProc, 0, intervalInMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
    196             }
    197         }
    198     }
    199 
    200     if (timerSet) {
    201         if (timerID) {
    202             KillTimer(timerWindowHandle, timerID);
    203             timerID = 0;
    204         }
    205     } else {
    206         timerID = SetTimer(timerWindowHandle, sharedTimerID, intervalInMS, 0);
    207         timer = 0;
    208     }
    209 }
    210 
    211 void stopSharedTimer()
    212 {
    213     if (timerQueue && timer) {
    214         DeleteTimerQueueTimer(timerQueue, timer, 0);
    215         timer = 0;
    216     }
    217 
    218     if (timerID) {
    219         KillTimer(timerWindowHandle, timerID);
    220         timerID = 0;
    221     }
    222 }
    223 
    224 }
    225