|
|
/****************************** Module Header ******************************\
* Module Name: cursor.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * This module contains code for dealing with cursors. * * History: * 03-Dec-1990 DavidPe Created. * 01-Feb-1991 MikeKe Added Revalidation code (None) * 12-Feb-1991 JimA Added access checks * 21-Jan-1992 IanJa ANSI/Unicode neutralized (null op) * 02-Aug-1992 DarrinM Added animated cursor code \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* zzzSetCursor (API) * * This API sets the cursor image for the current thread. * * History: * 12-03-90 DavidPe Created. \***************************************************************************/
PCURSOR zzzSetCursor( PCURSOR pcur) { PQ pq; PCURSOR pcurPrev; PTHREADINFO ptiCurrent = PtiCurrent();
pq = ptiCurrent->pq;
pcurPrev = pq->spcurCurrent;
if (pq->spcurCurrent != pcur) {
/*
* Lock() returns pobjOld - if it is still valid. Don't want to * return a pcurPrev that is an invalid pointer. */ pcurPrev = LockQCursor(pq, pcur);
/*
* If no thread 'owns' the cursor, we must be in initialization * so go ahead and assign it to ourself. */ if (gpqCursor == NULL) gpqCursor = pq;
/*
* If we're changing the local-cursor for the thread currently * representing the global-cursor, update the cursor image now. */ if (pq == gpqCursor) { TL tlpcur; ThreadLockWithPti(ptiCurrent, pcurPrev, &tlpcur); zzzUpdateCursorImage(); pcurPrev = ThreadUnlock(&tlpcur); } }
return pcurPrev; }
/***************************************************************************\
* zzzSetCursorPos (API) * * This API sets the cursor position. * * History: * 03-Dec-1990 DavidPe Created. * 12-Feb-1991 JimA Added access check * 16-May-1991 mikeke Changed to return BOOL \***************************************************************************/
BOOL zzzSetCursorPos( int x, int y) { /*
* Blow it off if the caller doesn't have the proper access rights */ if (!CheckWinstaWriteAttributesAccess()) { return FALSE; }
zzzInternalSetCursorPos(x, y);
/*
* Save the absolute coordinates in the global array * for GetMouseMovePointsEx. */ SAVEPOINT(x, y, SYSMET(CXVIRTUALSCREEN) - 1, SYSMET(CYVIRTUALSCREEN) - 1, NtGetTickCount(), 0);
return TRUE; }
/***************************************************************************\
* zzzInternalSetCursorPos * * This function is used whenever the server needs to set the cursor * position, regardless of the caller's access rights. * * History: * 12-Feb-1991 JimA Created. \***************************************************************************/ VOID zzzInternalSetCursorPos( int x, int y ) {
gptCursorAsync.x = x; gptCursorAsync.y = y;
BoundCursor(&gptCursorAsync);
gpsi->ptCursor = gptCursorAsync; /*
* Pass MP_PROCEDURAL as the last parameter so that in the * remote case we send an updated mouse position to the client */ GreMovePointer(gpDispInfo->hDev, gpsi->ptCursor.x, gpsi->ptCursor.y, MP_PROCEDURAL);
/*
* Cursor has changed position, so generate a mouse event so the * window underneath the new location knows it's there and sets the * shape accordingly. */ zzzSetFMouseMoved(); }
/***************************************************************************\
* IncCursorLevel * DecCursorLevel * * Keeps track of this thread show/hide cursor level as well as the queue * it is associated with. Thread levels are done so that when * AttachThreadInput() is called we can do exact level calculations in the * new queue. * * 15-Jan-1993 ScottLu Created. \***************************************************************************/
VOID IncCursorLevel( PTHREADINFO pti) { pti->iCursorLevel++; pti->pq->iCursorLevel++; }
VOID DecCursorLevel( PTHREADINFO pti) { pti->iCursorLevel--; pti->pq->iCursorLevel--; }
/***************************************************************************\
* zzzShowCursor (API) * * This API allows the application to hide or show the cursor image. * * History: * 03-Dec-1990 JimA Implemented for fake cursor stuff \***************************************************************************/
int zzzShowCursor( BOOL fShow) { PTHREADINFO pti = PtiCurrent(); PQ pq; int iCursorLevel;
pq = pti->pq; /*
* To preserve pq */ DeferWinEventNotify();
if (fShow) {
IncCursorLevel(pti);
if ((pq == gpqCursor) && (pq->iCursorLevel == 0)) zzzUpdateCursorImage();
} else {
DecCursorLevel(pti);
if ((pq == gpqCursor) && (pq->iCursorLevel == -1)) zzzUpdateCursorImage(); }
iCursorLevel = pq->iCursorLevel; zzzEndDeferWinEventNotify();
return iCursorLevel; }
/***************************************************************************\
* zzzClipCursor (API) * * This API sets the cursor clipping rectangle which restricts where the * cursor can go. If prcClip is NULL, the clipping rectangle will be the * screen. * * History: * 03-Dec-1990 DavidPe Created. * 16-May-1991 MikeKe Changed to return BOOL \***************************************************************************/
BOOL zzzClipCursor( LPCRECT prcClip) { PEPROCESS Process = PsGetCurrentProcess();
/*
* Don't let this happen if it doesn't have access. */ if (Process != gpepCSRSS && !CheckWinstaWriteAttributesAccess()) { return FALSE; }
/*
* The comment from NT 3.51: * Non-foreground threads can only set the clipping rectangle * if it was empty, or if they are restoring it to the whole screen. * * But the code from NT 3.51 says "IsRectEmpty" instead of * "!IsRectEmpty", as would follow from the comment. We leave * the code as it was, as following the comment appears to * break apps. * * CONSIDER: Removing this test altogether after NT4.0 ships. */ if ( PtiCurrent()->pq != gpqForeground && prcClip != NULL && IsRectEmpty(&grcCursorClip)) { RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Access denied in _ClipCursor"); return FALSE; }
if (prcClip == NULL) {
grcCursorClip = gpDispInfo->rcScreen;
} else {
/*
* Never let our cursor leave the screen. Can't use IntersectRect() * because it doesn't allow rects with 0 width or height. */ grcCursorClip.left = max(gpDispInfo->rcScreen.left , prcClip->left); grcCursorClip.right = min(gpDispInfo->rcScreen.right , prcClip->right); grcCursorClip.top = max(gpDispInfo->rcScreen.top , prcClip->top); grcCursorClip.bottom = min(gpDispInfo->rcScreen.bottom, prcClip->bottom);
/*
* Check for invalid clip rect. */ if (grcCursorClip.left > grcCursorClip.right || grcCursorClip.top > grcCursorClip.bottom) {
grcCursorClip = gpDispInfo->rcScreen; } }
/*
* Update the cursor position if it's currently outside the * cursor clip-rect. */ if (!PtInRect(&grcCursorClip, gpsi->ptCursor)) { zzzInternalSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y); }
return TRUE; }
/***************************************************************************\
* BoundCursor * * This rountine 'clips' gptCursorAsync to be within rcCursorClip. This * routine treats rcCursorClip as non-inclusive so the bottom and right sides * get bound to rcCursorClip.bottom/right - 1. * * Is called in OR out of the USER critical section!! IANJA * * History: * 03-Dec-1990 DavidPe Created. \***************************************************************************/ #ifdef LOCK_MOUSE_CODE
#pragma alloc_text(MOUSE, BoundCursor)
#endif
VOID BoundCursor( LPPOINT lppt) { if (TEST_PUDF(PUDF_VDMBOUNDSACTIVE) && gspwndFullScreen != NULL) {
if (lppt->x < grcVDMCursorBounds.left) { lppt->x = grcVDMCursorBounds.left; } else if (lppt->x >= grcVDMCursorBounds.right) { lppt->x = grcVDMCursorBounds.right - 1; }
if (lppt->y < grcVDMCursorBounds.top) { lppt->y = grcVDMCursorBounds.top; } else if (lppt->y >= grcVDMCursorBounds.bottom) { lppt->y = grcVDMCursorBounds.bottom - 1; }
} else {
if (lppt->x < grcCursorClip.left) { lppt->x = grcCursorClip.left; } else if (lppt->x >= grcCursorClip.right) { lppt->x = grcCursorClip.right - 1; }
if (lppt->y < grcCursorClip.top) { lppt->y = grcCursorClip.top; } else if (lppt->y >= grcCursorClip.bottom) { lppt->y = grcCursorClip.bottom - 1; } }
/*
* If we have more than one monitor, then we need to clip the * cursor to a point on the desktop. */ if (!gpDispInfo->fDesktopIsRect) { ClipPointToDesktop(lppt); } }
/***************************************************************************\
* SetVDMCursorBounds * * This routine is needed so when a vdm is running, the mouse is not bounded * by the screen. This is so the vdm can correctly virtualize the DOS mouse * device driver. It can't deal with user always bounding to the screen, * so it sets wide open bounds. * * 20-May-1993 ScottLu Created. \***************************************************************************/
VOID SetVDMCursorBounds( LPRECT lprc) { if (lprc != NULL) {
/*
* Set grcVDMCursorBounds before TEST_PUDF(PUDF_VDMBOUNDSACTIVE), because * MoveEvent() calls BoundCursor() from outside the USER CritSect! */ grcVDMCursorBounds = *lprc; Win32MemoryBarrier(); // Ensure grcVDMCursorBounds is updated first.
SET_PUDF(PUDF_VDMBOUNDSACTIVE);
} else {
/*
* Turn vdm bounds off. */ CLEAR_PUDF(PUDF_VDMBOUNDSACTIVE); }
/*
* Before returning from this function, * make sure the write instructions are all retired. */ Win32MemoryBarrier(); }
/***************************************************************************\
* zzzAnimateCursor * * When an animated cursor is loaded and the wait cursor is up this routine * gets called to maintain the cursor animation. * * Should only be called by the cursor animation timer. * * History: * 02-Oct-1991 DarrinM Created. * 03-Aug-1994 SanfordS Calibrated. \***************************************************************************/
#if defined (_M_IX86) && (_MSC_VER <= 1100)
#pragma optimize("s", off)
#endif
VOID zzzAnimateCursor( PWND pwndDummy, UINT message, UINT_PTR nID, LPARAM lParam) { int iicur; PACON pacon; TL tlpacon; int LostTime; int tTime;
pacon = (PACON)gpcurLogCurrent;
if (pacon == NULL || !(pacon->CURSORF_flags & CURSORF_ACON)) { gdwLastAniTick = 0; return; }
/*
* Find out actual time loss since last update. */ if (gdwLastAniTick) {
LostTime = NtGetTickCount() - gdwLastAniTick - (pacon->ajifRate[pacon->iicur] * 100 / 6);
if (LostTime < 0) LostTime = 0;
} else {
LostTime = 0; }
/*
* Increment the animation index. */ iicur = pacon->iicur + 1; if (iicur >= pacon->cicur) iicur = 0;
pacon->iicur = iicur;
/*
* This forces the new cursor to be drawn. */ ThreadLockAlways(pacon, &tlpacon); zzzUpdateCursorImage();
tTime = pacon->ajifRate[iicur] * 100 / 6;
while (tTime < LostTime) {
/*
* Animation is outrunning our ability to render it - skip frames * to catch up. */ LostTime -= tTime;
/*
* Increment the animation index. */ iicur = pacon->iicur + 1; if (iicur >= pacon->cicur) iicur = 0;
pacon->iicur = iicur;
tTime = pacon->ajifRate[iicur] * 100 / 6; } ThreadUnlock(&tlpacon);
gdwLastAniTick = NtGetTickCount() - LostTime; gidCursorTimer = InternalSetTimer(NULL, gidCursorTimer, tTime - LostTime, zzzAnimateCursor, TMRF_RIT | TMRF_ONESHOT);
return;
DBG_UNREFERENCED_PARAMETER(pwndDummy); DBG_UNREFERENCED_PARAMETER(message); DBG_UNREFERENCED_PARAMETER(nID); DBG_UNREFERENCED_PARAMETER(lParam); }
/**************************************************************************\
* FCursorShadowed * \**************************************************************************/
__inline FCursorShadowed(PCURSINFO pci) { return (TestALPHA(CURSORSHADOW) && (pci->CURSORF_flags & CURSORF_SYSTEM)); }
#if defined (_M_IX86) && (_MSC_VER <= 1100)
#pragma optimize("", on)
#endif
/**************************************************************************\
* zzzUpdateCursorImage * * History: * 14-Jan-1992 DavidPe Created. * 09-Aug-1992 DarrinM Added animated cursor code. * 01-Oct-2000 JasonSch Added autorun cursor code. \**************************************************************************/ VOID zzzUpdateCursorImage() { PCURSOR pcurLogNew; PCURSOR pcurPhysNew; PACON pacon; PCURSOR pcurPhysOld; DWORD event;
#ifdef GENERIC_INPUT
/*
* WindowsBug 298252 * Even though the mouse pointer is outside * the GenericInput aware app, allow it to hide * the mouse cursor if mouse is captured. * i.e. use gpqForeground instead of gpqCursor. */ if (gpqForeground) { PTHREADINFO ptiMouse = PtiMouseFromQ(gpqForeground);
if (TestRawInputMode(ptiMouse, CaptureMouse)) { if (gpqForeground->iCursorLevel < 0) { pcurLogNew = NULL; goto force_setnull; } } } #endif
if (gpqCursor == NULL) return;
if ((gpqCursor->iCursorLevel < 0) || (gpqCursor->spcurCurrent == NULL)) {
pcurLogNew = NULL;
} else {
/*
* Assume we're using the current cursor. */ pcurLogNew = gpqCursor->spcurCurrent;
/*
* Check to see if we should use the "app starting" or the "autorun" * cursor. */ if (gtimeStartCursorHide != 0 #ifdef AUTORUN_CURSOR
|| gtmridAutorunCursor != 0 #endif // AUTORUN_CURSOR
) {
if (gpqCursor->spcurCurrent == SYSCUR(ARROW) || #ifdef AUTORUN_CURSOR
gpqCursor->spcurCurrent == SYSCUR(AUTORUN) || #endif // AUTORUN_CURSOR
gpqCursor->spcurCurrent == SYSCUR(APPSTARTING)) {
#ifdef AUTORUN_CURSOR
if (gtmridAutorunCursor != 0) { pcurLogNew = SYSCUR(AUTORUN); } else { #endif // AUTORUN_CURSOR
pcurLogNew = SYSCUR(APPSTARTING); #ifdef AUTORUN_CURSOR
} #endif // AUTORUN_CURSOR
} } }
#ifdef GENERIC_INPUT
force_setnull: #endif
/*
* If the logical cursor is changing then start/stop the cursor * animation timer as appropriate. */ if (pcurLogNew != gpcurLogCurrent) {
/*
* If the old cursor was animating, shut off the animation timer. */ if (gtmridAniCursor != 0) { /*
* Disable animation. */ KILLRITTIMER(NULL, gtmridAniCursor); gtmridAniCursor = 0; }
/*
* If the new cursor is animated, start the animation timer. */ if ((pcurLogNew != NULL) && (pcurLogNew->CURSORF_flags & CURSORF_ACON)) {
/*
* Start the animation over from the beginning. */ pacon = (PACON)pcurLogNew; pacon->iicur = 0;
gdwLastAniTick = NtGetTickCount();
/*
* Use the rate table to keep the timer on track. * 1 Jiffy = 1/60 sec = 100/6 ms */ gtmridAniCursor = InternalSetTimer(NULL, gtmridAniCursor, pacon->ajifRate[0] * 100 / 6, zzzAnimateCursor, TMRF_RIT | TMRF_ONESHOT); } }
/*
* If this is an animated cursor, find and use the current frame * of the animation. NOTE: this is done AFTER the AppStarting * business so the AppStarting cursor itself can be animated. */ if (pcurLogNew != NULL && pcurLogNew->CURSORF_flags & CURSORF_ACON) {
pcurPhysNew = ((PACON)pcurLogNew)->aspcur[((PACON)pcurLogNew)-> aicur[((PACON)pcurLogNew)->iicur]]; } else {
pcurPhysNew = pcurLogNew; }
/*
* Remember the new logical cursor. */ gpcurLogCurrent = pcurLogNew;
/*
* If the physical cursor is changing then update screen. */ if (pcurPhysNew != gpcurPhysCurrent) {
pcurPhysOld = gpcurPhysCurrent;
gpcurPhysCurrent = pcurPhysNew;
if (pcurPhysNew == NULL) {
SetPointer(FALSE);
} else { ULONG fl = 0;
if (pcurLogNew->CURSORF_flags & CURSORF_ACON) { fl |= SPS_ANIMATEUPDATE; } if (FCursorShadowed(GETPCI(pcurLogNew))) { fl |= SPS_ALPHA; } GreSetPointer(gpDispInfo->hDev, GETPCI(pcurPhysNew), fl, GETMOUSETRAILS(), MOUSE_TRAILS_FREQ); }
/*
* Notify anyone who cares about the change * This can happen on the RIT, so we need to pass on the real * thread/process ID. Hence we use hwndCursor. * This comment is from WIn'95 so it may not be true - IanJa. * * These are the events we send: * hcurPhys now NULL -> EVENT_OBJECT_HIDE * hcurPhys was NULL -> EVENT_OBJECT_SHOW * hcurPhys changing -> EVENT_OBJECT_NAMECHANGE * Since we only go through this code if hcurPhys is actually * changing, these checks are simple. */ if (!pcurPhysNew) { event = EVENT_OBJECT_HIDE; } else if (!pcurPhysOld) { event = EVENT_OBJECT_SHOW; } else { event = EVENT_OBJECT_NAMECHANGE; }
zzzWindowEvent(event, NULL, OBJID_CURSOR, INDEXID_CONTAINER, WEF_USEPWNDTHREAD); } }
#if DBG
/***************************************************************************\
* DbgLockQCursor * * Special routine to lock cursors into a queue. Besides a pointer * to the cursor, the handle is also saved. * Returns the pointer to the previous current cursor for that queue. * * History: * 26-Jan-1993 JimA Created. \***************************************************************************/
PCURSOR DbgLockQCursor( PQ pq, PCURSOR pcur) { /*
* See if the queue is marked for destuction. If so, we should not * be trying to lock a cursor. */ UserAssertMsg0(!(pq->QF_flags & QF_INDESTROY), "LockQCursor: Attempting to lock cursor to freed queue");
return Lock(&pq->spcurCurrent, pcur); }
#endif // DBG
/***************************************************************************\
* SetPointer * * 29-Mar-1998 vadimg created \***************************************************************************/
void SetPointer(BOOL fSet) { if (fSet) { #ifdef GENERIC_INPUT
if (gpqForeground) { PTHREADINFO ptiMouse = PtiMouseFromQ(gpqForeground);
if (gpqForeground->iCursorLevel < 0 && TestRawInputMode(ptiMouse, CaptureMouse)) { return; } } #endif
if (gpqCursor != NULL && gpqCursor->iCursorLevel >= 0 && gpqCursor->spcurCurrent != NULL && SYSMET(MOUSEPRESENT)) {
PCURSINFO pci = GETPCI(gpqCursor->spcurCurrent); ULONG fl = FCursorShadowed(pci) ? SPS_ALPHA : 0;
GreSetPointer(gpDispInfo->hDev, pci, fl, GETMOUSETRAILS(), MOUSE_TRAILS_FREQ); } } else { GreSetPointer(gpDispInfo->hDev, NULL, 0, 0, 0); } }
/***************************************************************************\
* HideCursorNoCapture * * Set the cursor to NULL if the mouse is not captured * * 20-May-1998 MCostea created \***************************************************************************/ VOID zzzHideCursorNoCapture() { PTHREADINFO ptiCurrent = PtiCurrentShared();
if (!ptiCurrent->pq->spwndCapture && ((GetAppCompatFlags2(VER40) & GACF2_EDITNOMOUSEHIDE) == 0) && TestEffectUP(MOUSEVANISH)) {
zzzSetCursor(NULL); } }
#ifdef AUTORUN_CURSOR
/***************************************************************************\
* ShowAutorunCursor * * Kicks off a system timer that will fire when it's time to hide the autorun * cursor and calls zzzUpdateCursorImage to change the current cursor to the * autorun guy. * * 02-Oct-2000 JasonSch created \***************************************************************************/ VOID ShowAutorunCursor( ULONG ulTimeout) { EnterCrit();
/*
* Create/reset the timer. If we're already set it and it hasn't yet gone * off, this will reset the time to whatever we specify (which is the * behavior we want). */ gtmridAutorunCursor = InternalSetTimer(NULL, gtmridAutorunCursor, ulTimeout, HideAutorunCursor, TMRF_RIT | TMRF_ONESHOT); LeaveCrit(); }
/***************************************************************************\
* HideAutorunCursor * * Destroys the autorun cursor timer and resets the cursor itself. * * 02-Oct-2000 JasonSch created \***************************************************************************/ VOID HideAutorunCursor( PWND pwnd, UINT message, UINT_PTR nID, LPARAM lParam) { CheckCritIn();
/*
* Calling zzzUpdateCursorImage with a NULL gtmridAutorunCursor will cause * the cursor to change to whatever is should be (e.g., app starting, if * appropriate). */ KILLRITTIMER(NULL, gtmridAutorunCursor); gtmridAutorunCursor = 0; zzzUpdateCursorImage();
UNREFERENCED_PARAMETER(pwnd); UNREFERENCED_PARAMETER(message); UNREFERENCED_PARAMETER(nID); UNREFERENCED_PARAMETER(lParam); } #endif // AUTORUN_CURSOR
|