Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1000 lines
30 KiB

/****************************** Module Header ******************************\
* Module Name: timers.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* This module contains the user timer APIs and support routines.
*
* History:
* 12-Nov-1990 DarrinM Created.
* 08-Apr-1992 DarrinM Switched to PM/Win3-like ScanTimers model.
\***************************************************************************/
#define _TIMERS 1 // uses a LARGE_INTEGER
#include "precomp.h"
#pragma hdrstop
/*
* Make sure that if we return a timer id that it is a WORD value. This
* will ensure that WOW doesn't need to handle-translate return values
* from SetTimer().
*
* Start with a large number so that FindTimer() doesn't find a timer we
* calculated with a low cTimerId if the app happens to pass in NULL pwnd
* and a low id (like 1).
*/
#define TIMERID_MAX 0x7FFF
#define TIMERID_MIN 0x100
#define ELAPSED_MAX 0x7FFFFFFF
#define SYSRIT_TIMER (TMRF_SYSTEM | TMRF_RIT)
WORD cTimerId = TIMERID_MAX;
/***************************************************************************\
* _SetTimer (API)
*
* This API will start the specified timer.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
UINT_PTR _SetTimer(
PWND pwnd,
UINT_PTR nIDEvent,
UINT dwElapse,
TIMERPROC_PWND pTimerFunc)
{
/*
* Prevent apps from setting a Timer with a window proc to another app
*/
if (pwnd && (PpiCurrent() != GETPTI(pwnd)->ppi)) {
RIPERR1(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Calling SetTimer with window of another process %lX",
pwnd);
return 0;
}
return InternalSetTimer(pwnd, nIDEvent, dwElapse, pTimerFunc, 0);
}
/***************************************************************************\
* _SetSystemTimer
*
* This API will start start a system timer which will generate WM_SYSTIMER
* messages rather than WM_TIMER
*
* History:
* 15-Nov-1990 DavidPe Created.
* 21-Jan-1991 IanJa Prefix '_' denotes export function (not API)
\***************************************************************************/
UINT_PTR _SetSystemTimer(
PWND pwnd,
UINT_PTR nIDEvent,
DWORD dwElapse,
TIMERPROC_PWND pTimerFunc)
{
/*
* Prevent apps from setting a Timer with a window proc to another app
*/
if (pwnd && PpiCurrent() != GETPTI(pwnd)->ppi) {
RIPERR1(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Calling SetSystemTimer with window of another process 0x%p",
pwnd);
return 0;
}
return InternalSetTimer(pwnd, nIDEvent, dwElapse, pTimerFunc, TMRF_SYSTEM);
}
/***************************************************************************\
* FreeTimer
*
* This function does the actual unlinking and freeing of the timer structure.
* I pulled it out of FindTimer() so it could be shared with DestroyQueues-
* Timers.
* Sets *pptmr to point to the next TIMER struct (NULL if none)
*
* History:
* 15-Feb-1991 DarrinM Pulled from FindTimer().
\***************************************************************************/
VOID FreeTimer(
PTIMER ptmr) {
CheckCritIn();
/*
* Mark it for destruction. If it the object is locked it can't
* be freed right now.
*/
if (!HMMarkObjectDestroy((PVOID)ptmr))
return;
/*
* If this timer was just about to be processed, decrement
* the ready-count since we're blowing it off.
*/
if (ptmr->flags & TMRF_READY)
DecTimerCount(ptmr->pti);
/*
* Unlock the window
*/
Unlock(&ptmr->spwnd);
/*
* Unlink this timer
*/
if (ptmr->ptmrPrev) {
ptmr->ptmrPrev->ptmrNext = ptmr->ptmrNext;
} else {
gptmrFirst = ptmr->ptmrNext;
}
if (ptmr->ptmrNext) {
ptmr->ptmrNext->ptmrPrev = ptmr->ptmrPrev;
}
/*
* Free up the TIMER structure.
*/
HMFreeObject((PVOID)ptmr);
}
/***************************************************************************\
* FindTimer
*
* This function will find a timer that matches the parameters. We also
* deal with killing timers here since it's easier to remove items from
* the list while we're scanning it.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
PTIMER FindTimer(
PWND pwnd,
UINT_PTR nID,
UINT flags,
BOOL fKill)
{
PTIMER ptmr;
ptmr = gptmrFirst;
while (ptmr != NULL) {
/*
* Is this the timer we're looking for?
*/
if ((ptmr->spwnd == pwnd) &&
(ptmr->nID == nID) &&
(ptmr->flags & SYSRIT_TIMER) == (flags & SYSRIT_TIMER)) {
/*
* Are we being called from KillTimer()? If so, destroy the
* timer. return != 0 because *pptmr is gone.
*/
if (fKill) {
FreeTimer(ptmr);
return (PTIMER)TRUE;
}
/*
* Found the timer, break out of the loop.
*/
break;
}
/*
* No, try the next one.
*/
ptmr = ptmr->ptmrNext;
}
return ptmr;
}
/***************************************************************************\
* InternalSetTimer
*
* This is the guts of SetTimer that actually gets things going.
*
* NOTE (darrinm): Technically there is a bit of latency (the time it takes
* between SetTimer's NtSetEvent and when the RIT wakes up and calls ScanTimers)
* between when SetTimer is called and when the counter starts counting down.
* This is uncool but it should be a very short amount of time because the RIT
* is high-priority. If it becomes a problem I know how to fix it.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
UINT_PTR InternalSetTimer(
PWND pwnd,
UINT_PTR nIDEvent,
UINT dwElapse,
TIMERPROC_PWND pTimerFunc,
UINT flags)
{
LARGE_INTEGER liT = {1, 0};
PTIMER ptmr;
PTHREADINFO ptiCurrent;
CheckCritIn();
/*
* Assert if someone tries to set a timer after cleanup initiated.
*/
if (gbCleanupInitiated) {
RIPMSGF0(RIP_ERROR, "Too late to create a timer.");
return 0;
}
/*
* We need to make sure dwElapse isn't too big. NtUserSetTimer ensures
* that no app passes in zero for the timeout value; let's assert here
* that that remains the case, and that no one internally does this.
*/
UserAssert(dwElapse != 0);
if (dwElapse > ELAPSED_MAX) {
RIPMSGF1(RIP_WARNING,
"Timer period (0x%x) is too big",
dwElapse);
dwElapse = ELAPSED_MAX;
}
/*
* Attempt to first locate the timer, then create a new one
* if one isn't found.
*/
if ((ptmr = FindTimer(pwnd, nIDEvent, flags, FALSE)) == NULL) {
/*
* Not found. Create a new one.
*/
ptmr = (PTIMER)HMAllocObject(NULL, NULL, TYPE_TIMER, sizeof(TIMER));
if (ptmr == NULL) {
return 0;
}
ptmr->spwnd = NULL;
if (pwnd == NULL) {
WORD timerIdInitial = cTimerId;
/*
* Pick a unique, unused timer ID.
*/
do {
if (--cTimerId <= TIMERID_MIN) {
cTimerId = TIMERID_MAX;
}
if (cTimerId == timerIdInitial) {
/*
* Flat out of timers bud.
*/
HMFreeObject(ptmr);
return 0;
}
} while (FindTimer(NULL, cTimerId, flags, FALSE) != NULL);
ptmr->nID = (UINT)cTimerId;
} else {
ptmr->nID = nIDEvent;
}
/*
* Link the new timer into the front of the list.
* Handily this works even when gptmrFirst is NULL.
*/
ptmr->ptmrNext = gptmrFirst;
ptmr->ptmrPrev = NULL;
if (gptmrFirst) {
gptmrFirst->ptmrPrev = ptmr;
}
gptmrFirst = ptmr;
} else {
/*
* If this timer was just about to be processed, decrement
* cTimersReady since we're resetting it.
*/
if (ptmr->flags & TMRF_READY) {
DecTimerCount(ptmr->pti);
}
}
/*
* If pwnd is NULL, create a unique id by
* using the timer handle. RIT timers are 'owned' by the RIT pti
* so they are not deleted when the creating pti dies.
*
* We used to record the pti as the pti of the window if one was
* specified. This is not what Win 3.1 does and it broke 10862
* where some merge app was setting the timer on winword's window
* it it still expected to get the messages not winword.
*
* MS Visual C NT was counting on this bug in the NT 3.1 so if
* a thread sets a timer for a window in another thread in the
* same process the timer goes off in the thread of the window.
* You can see this by doing a build in msvcnt and the files being
* compiled do not show up.
*/
ptiCurrent = (PTHREADINFO)(W32GetCurrentThread()); /*
* This will be NULL
* for a non-GUI thread.
*/
if (pwnd == NULL) {
if (flags & TMRF_RIT) {
ptmr->pti = gptiRit;
} else {
ptmr->pti = ptiCurrent;
UserAssert(ptiCurrent);
}
} else {
/*
* As enforced in the API wrappers. We shouldn't get here
* any other way for an app timer.
*
* Always use pti of the window when TMRF_PTIWINDOW is passed in.
*/
if ((ptiCurrent->TIF_flags & TIF_16BIT) && !(flags & TMRF_PTIWINDOW)) {
ptmr->pti = ptiCurrent;
UserAssert(ptiCurrent);
} else {
ptmr->pti = GETPTI(pwnd);
}
}
/*
* Initialize the timer-struct.
*
* NOTE: The ptiOptCreator is used to identify a JOURNAL-timer. We
* want to allow these timers to be destroyed when the creator
* thread goes away. For other threads that create timers across
* threads, we do not want to destroy these timers when the
* creator goes away. Currently, we're only checking for a
* TMRF_RIT. However, in the future we might want to add this
* same check for TMRF_SYSTEM.
*/
Lock(&(ptmr->spwnd), pwnd);
ptmr->cmsCountdown = ptmr->cmsRate = dwElapse;
ptmr->flags = flags | TMRF_INIT;
ptmr->pfn = pTimerFunc;
ptmr->ptiOptCreator = (flags & TMRF_RIT ? ptiCurrent : NULL);
/*
* Force the RIT to scan timers.
*
* N.B. The following code sets the raw input thread timer to expire
* at the absolute time 1 which is very far into the past. This
* causes the timer to immediately expire before the set timer
* call returns.
*/
if (ptiCurrent == gptiRit) {
/*
* Don't let RIT timer loop reset the master timer - we already have.
*/
gbMasterTimerSet = TRUE;
}
UserAssert(gptmrMaster);
KeSetTimer(gptmrMaster, liT, NULL);
/*
* Windows 3.1 returns the timer ID if non-zero, otherwise it returns 1.
*/
return (ptmr->nID == 0 ? 1 : ptmr->nID);
}
/***************************************************************************\
* _KillTimer (API)
*
* This API will stop a timer from sending WM_TIMER messages.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
BOOL _KillTimer(
PWND pwnd,
UINT_PTR nIDEvent)
{
return KillTimer2(pwnd, nIDEvent, FALSE);
}
/***************************************************************************\
* _KillSystemTimer
*
* This API will stop a system timer from sending WM_SYSTIMER messages.
*
* History:
* 15-Nov-1990 DavidPe Created.
* 21-Jan-1991 IanJa Prefix '_' denotes export function (not API)
\***************************************************************************/
BOOL _KillSystemTimer(
PWND pwnd,
UINT_PTR nIDEvent)
{
return KillTimer2(pwnd, nIDEvent, TRUE);
}
/***************************************************************************\
* KillTimer2
*
* This is the guts of KillTimer that actually kills the timer.
*
* History:
* 15-Nov-1990 DavidPe Created.
\***************************************************************************/
BOOL KillTimer2(
PWND pwnd,
UINT_PTR nIDEvent,
BOOL fSystemTimer)
{
/*
* Call FindTimer() with fKill == TRUE. This will
* basically delete the timer.
*/
return (FindTimer(pwnd,
nIDEvent,
(fSystemTimer ? TMRF_SYSTEM : 0),
TRUE) != NULL);
}
/***************************************************************************\
* DestroyQueuesTimers
*
* This function scans through all the timers and destroys any that are
* associated with the specified queue.
*
* History:
* 15-Feb-1991 DarrinM Created.
\***************************************************************************/
VOID DestroyThreadsTimers(
PTHREADINFO pti)
{
PTIMER ptmr;
ptmr = gptmrFirst;
while (ptmr != NULL) {
/*
* Is this one of the timers we're looking for? If so, destroy it.
*/
if (ptmr->pti == pti || ptmr->ptiOptCreator == pti) {
PTIMER ptmrNext = ptmr->ptmrNext;
FreeTimer(ptmr);
ptmr = ptmrNext;
} else {
ptmr = ptmr->ptmrNext;
}
}
}
/***************************************************************************\
* DestroyWindowsTimers
*
* This function scans through all the timers and destroys any that are
* associated with the specified window.
*
* History:
* 04-Jun-1991 DarrinM Created.
\***************************************************************************/
VOID DestroyWindowsTimers(
PWND pwnd)
{
PTIMER ptmr;
ptmr = gptmrFirst;
while (ptmr != NULL) {
/*
* Is this one of the timers we're looking for? If so, destroy it.
*/
if (ptmr->spwnd == pwnd) {
PTIMER ptmrNext = ptmr->ptmrNext;
FreeTimer(ptmr);
ptmr = ptmrNext;
} else {
ptmr = ptmr->ptmrNext;
}
}
}
/***************************************************************************\
* DoTimer
*
* This function gets called from xxxPeekMessage() if the QS_TIMER bit is
* set. If this timer is okay with the filter specified the appropriate
* WM_*TIMER message will be placed in 'pmsg' and the timer will be reset.
*
* History:
* 15-Nov-1990 DavidPe Created.
* 27-NOv-1991 DavidPe Changed to move 'found' timers to end of list.
\***************************************************************************/
BOOL DoTimer(
PWND pwndFilter)
{
PTHREADINFO pti;
PTIMER ptmr;
PTIMER ptmrNext;
PQMSG pqmsg;
CheckCritIn();
pti = PtiCurrent();
/*
* Search for a timer that belongs to this queue.
*/
ptmr = gptmrFirst;
while (ptmr != NULL) {
/*
* Has this timer gone off and is it one we're looking for?
*/
if ((ptmr->flags & TMRF_READY) &&
(ptmr->pti == pti) &&
CheckPwndFilter(ptmr->spwnd, pwndFilter)) {
/*
* We found an appropriate timer. Put it in the app's queue and
* return success.
*/
if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
/*
* Store the message and set the QS_POSTMESSAGE bit so the
* thread knows it has a message.
*/
StoreQMessage(pqmsg,
ptmr->spwnd,
(UINT)((ptmr->flags & TMRF_SYSTEM) ?
WM_SYSTIMER : WM_TIMER),
(WPARAM)ptmr->nID,
(LPARAM)ptmr->pfn,
0, 0, 0);
#ifdef REDIRECTION
StoreQMessagePti(pqmsg, pti);
#endif // REDIRECTION
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
}
/*
* Reset this timer.
*/
ptmr->flags &= ~TMRF_READY;
DecTimerCount(ptmr->pti);
/*
* If there are other timers in the system move this timer
* to the end of the list so other timers in for this queue
* get a chance to go off.
*/
ptmrNext = ptmr->ptmrNext;
if (ptmrNext != NULL) {
/*
* Remove ptmr from its place in the list.
*/
if (ptmr->ptmrPrev) {
ptmr->ptmrPrev->ptmrNext = ptmr->ptmrNext;
} else
gptmrFirst = ptmr->ptmrNext;
ptmrNext->ptmrPrev = ptmr->ptmrPrev;
/*
* Move to the last TIMER of the list.
*/
while (ptmrNext->ptmrNext != NULL)
ptmrNext = ptmrNext->ptmrNext;
/*
* Insert this timer at the end.
*/
ptmrNext->ptmrNext = ptmr;
ptmr->ptmrPrev = ptmrNext;
ptmr->ptmrNext = NULL;
}
return TRUE;
}
ptmr = ptmr->ptmrNext;
}
return FALSE;
}
/***************************************************************************\
* DecTimerCount
*
* This routine decrements cTimersReady and clears QS_TIMER if the count
* goes down to zero.
*
* History:
* 21-Jan-1991 DavidPe Created.
\***************************************************************************/
VOID DecTimerCount(
PTHREADINFO pti)
{
CheckCritIn();
if (--pti->cTimersReady == 0)
pti->pcti->fsWakeBits &= ~QS_TIMER;
}
/***************************************************************************\
* JournalTimer
*
*
* History:
* 04-Mar-1991 DavidPe Created.
\***************************************************************************/
VOID JournalTimer(
PWND pwnd,
UINT message,
UINT_PTR nID,
LPARAM lParam)
{
PTHREADINFO pti;
DBG_UNREFERENCED_PARAMETER(pwnd);
DBG_UNREFERENCED_PARAMETER(message);
DBG_UNREFERENCED_PARAMETER(nID);
/*
* We've already entered the critical section.
*/
if (pti = ((PTIMER)lParam)->ptiOptCreator)
WakeSomeone(pti->pq, pti->pq->msgJournal, NULL);
return;
}
/***************************************************************************\
* SetJournalTimer
*
* Sets an NT timer that goes off in 'dt' milliseconds and will wake
* up 'pti' at that time. This is used in journal playback code to
* simulate the timing in which events were originally given to the system.
*
* History:
* 04-Mar-1991 DavidPe Created.
\***************************************************************************/
void SetJournalTimer(
DWORD dt,
UINT msgJournal)
{
static UINT_PTR idJournal = 0;
PtiCurrent()->pq->msgJournal = msgJournal;
/*
* Remember idJournal - because TMRF_ONESHOT timers stay in the timer
* list - by remembering the idJournal, we always reuse the same timer
* rather than creating new ones always.
*/
idJournal = InternalSetTimer(NULL,
idJournal,
dt,
JournalTimer,
TMRF_RIT | TMRF_ONESHOT);
}
/***************************************************************************\
* StartTimers
*
* Prime the timer pump by starting the cursor restoration timer.
*
* History:
* 02-Apr-1992 DarrinM Created.
\***************************************************************************/
UINT_PTR StartTimers(VOID)
{
/*
* Let GDI know that it can start settings timers on the RIT.
*/
GreStartTimers();
/*
* TMRF_RIT timers are called directly from ScanTimers -- no nasty
* thread switching for these boys.
*/
return InternalSetTimer(NULL, 0, 1000, xxxHungAppDemon, TMRF_RIT);
}
/***************************************************************************\
* TimersProc
*
* Deal with the timers. Called from RawInputThread.
*
* History:
* 11-11-1996 CLupu Created.
\***************************************************************************/
VOID TimersProc(
VOID)
{
DWORD dmsSinceLast, cmsCur, dmsNextTimer;
LARGE_INTEGER liT;
PTIMER ptmr;
EnterCrit();
/*
* Calculate how long it was since the last time we processed timers so
* we can subtract that much time from each timer's countdown value.
*/
cmsCur = NtGetTickCount();
dmsSinceLast = ComputePastTickDelta(cmsCur, gcmsLastTimer);
gcmsLastTimer = cmsCur;
/*
* dmsNextTimer is the time delta before the next
* timer should go off. As we loop through the
* timers below this will shrink to the smallest
* cmsCountdown value in the list.
*/
dmsNextTimer = ELAPSED_MAX;
ptmr = gptmrFirst;
gbMasterTimerSet = FALSE;
while (ptmr != NULL) {
/*
* ONESHOT timers go to a WAITING state after
* they go off. This allows us to leave them
* in the list but keep them from going off
* over and over.
*/
if (ptmr->flags & TMRF_WAITING) {
ptmr = ptmr->ptmrNext;
continue;
}
/*
* The first time we encounter a timer we don't
* want to set it off, we just want to use it to
* compute the shortest countdown value.
*/
if (ptmr->flags & TMRF_INIT) {
ptmr->flags &= ~TMRF_INIT;
} else {
/*
* If this timer is going off, wake up its
* owner.
*/
if (ptmr->cmsCountdown > dmsSinceLast) {
ptmr->cmsCountdown -= dmsSinceLast;
} else {
UserAssert(ptmr->cmsRate <= ELAPSED_MAX);
ptmr->cmsCountdown = ptmr->cmsRate;
/*
* If the timer's owner hasn't handled the
* last time it went off yet, throw this event
* away.
*/
if (!(ptmr->flags & TMRF_READY)) {
/*
* A ONESHOT timer goes into a WAITING state
* until SetTimer is called again to reset it.
*/
if (ptmr->flags & TMRF_ONESHOT)
ptmr->flags |= TMRF_WAITING;
/*
* RIT timers have the distinction of being
* called directly and executing serially with
* with incoming timer events.
* NOTE: RIT timers get called while we're
* inside the critical section.
*/
if (ptmr->flags & TMRF_RIT) {
TL tlTimer;
ThreadLock(ptmr, &tlTimer);
/*
* May set gbMasterTimerSet
*/
(ptmr->pfn)(NULL,
WM_SYSTIMER,
ptmr->nID,
(LPARAM)ptmr);
if (HMIsMarkDestroy(ptmr)) {
ptmr = ptmr->ptmrNext;
ThreadUnlock(&tlTimer);
continue;
}
ThreadUnlock(&tlTimer);
} else {
ptmr->flags |= TMRF_READY;
ptmr->pti->cTimersReady++;
SetWakeBit(ptmr->pti, QS_TIMER);
}
}
}
}
/*
* Remember the shortest time left of the timers.
*/
if (ptmr->cmsCountdown < dmsNextTimer) {
dmsNextTimer = ptmr->cmsCountdown;
}
/*
* Advance to the next timer structure.
*/
ptmr = ptmr->ptmrNext;
}
if (!gbMasterTimerSet) {
/*
* Time in NT should be negative to specify a relative
* time. It's also in hundred nanosecond units so multiply
* by 10000 to get the right value from milliseconds.
*/
liT.QuadPart = Int32x32To64(-10000, dmsNextTimer);
KeSetTimer(gptmrMaster, liT, NULL);
}
LeaveCrit();
}
/***************************************************************************\
* xxxSystemTimerProc()
*
* 11/15/96 GerardoB Created
\***************************************************************************/
VOID xxxSystemTimerProc(PWND pwnd, UINT msg, UINT_PTR id, LPARAM lParam)
{
CheckLock(pwnd);
UNREFERENCED_PARAMETER(msg);
UNREFERENCED_PARAMETER(id);
UNREFERENCED_PARAMETER(lParam);
switch (id) {
case IDSYS_LAYER: {
PDCE pdce;
UserAssert(gnVisibleRedirectedCount > 0);
for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) {
if (pdce->DCX_flags & (DCX_INVALID | DCX_DESTROYTHIS))
continue;
if ((pdce->DCX_flags & DCX_REDIRECTED) && (pdce->DCX_flags & DCX_INUSE)) {
UpdateRedirectedDC(pdce);
}
}
}
return;
case IDSYS_FADE:
AnimateFade();
return;
case IDSYS_FLASHWND:
xxxFlashWindow(pwnd, FLASHW_TIMERCALL, 0);
return;
case IDSYS_WNDTRACKING: {
/*
* If the active track window hasn't changed,
* it's time to active it.
* spwndTrack can be NULL if it got destroyed but we haven't
* destroyed the timer.yet
*/
PTHREADINFO pti = GETPTI(pwnd);
UserAssert(TestUP(ACTIVEWINDOWTRACKING));
if ((pti->rpdesk->spwndTrack != NULL)
&& (pwnd == GetActiveTrackPwnd(pti->rpdesk->spwndTrack, NULL))) {
pti->pq->QF_flags |= (QF_ACTIVEWNDTRACKING | QF_MOUSEMOVED);
#ifdef REDIRECTION
/*
* Should we call the hit test hook here ?
*/
PushMouseMove(pti->pq, gpsi->ptCursor);
#endif // REDIRECTION
SetWakeBit(pti, QS_MOUSEMOVE);
}
}
break;
case IDSYS_MOUSEHOVER: {
PTHREADINFO pti = GETPTI(pwnd);
PDESKTOP pdesk = pti->rpdesk;
/*
* If hover hasn't been canceled, the mouse is still on
* this window and the point is still on the rect, then
* it's hover time!
*/
if ((pdesk->dwDTFlags & DF_TRACKMOUSEHOVER)
&& (HWq(pwnd) == HWq(pdesk->spwndTrack)
&& PtInRect(&pdesk->rcMouseHover, gpsi->ptCursor))) {
UINT message;
WPARAM wParam;
POINT pt = gpsi->ptCursor;
if (pdesk->htEx == HTCLIENT) {
message = WM_MOUSEHOVER;
wParam = (WPARAM)GetMouseKeyFlags(pti->pq);
if (TestWF(pwnd, WEFLAYOUTRTL)) {
pt.x = pwnd->rcClient.right - pt.x - 1;
} else {
pt.x -= pwnd->rcClient.left;
}
pt.y -= pwnd->rcClient.top;
} else {
message = WM_NCMOUSEHOVER;
/*
* Map the extended hit test code to a public one.
*/
wParam = (WPARAM)LOWORD(pdesk->htEx);
if ((wParam >= HTEXMENUFIRST) && (wParam <= HTEXMENULAST)) {
wParam = (WPARAM)HTMENU;
} else if ((wParam >= HTEXSCROLLFIRST) && (wParam <= HTEXSCROLLLAST)) {
wParam = (WPARAM)(HIWORD(pdesk->htEx) ? HTVSCROLL : HTHSCROLL);
}
}
_PostMessage(pwnd, message, wParam, MAKELPARAM(pt.x, pt.y));
pdesk->dwDTFlags &= ~DF_TRACKMOUSEHOVER;
break;
}
}
return;
default:
RIPMSG1(RIP_ERROR, "xxxSystemTimerProc: unexpected id: 0x%x", id);
break;
}
/*
* If we fell through, the timer's got to go.
*/
_KillSystemTimer(pwnd, id);
}