mirror of https://github.com/lianthony/NT4.0
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.
625 lines
16 KiB
625 lines
16 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: cursor.c
|
|
*
|
|
* Copyright (c) 1985-1996, 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
|
|
|
|
/*
|
|
* Globals used only in his file.
|
|
*/
|
|
BOOL gfVDMBoundsActive = FALSE;
|
|
RECT grcVDMCursorBounds;
|
|
DWORD gdwLastAniTick = 0;
|
|
|
|
static int idCursorTimer = 0;
|
|
|
|
/***************************************************************************\
|
|
* _SetCursor (API)
|
|
*
|
|
* This API sets the cursor image for the current thread.
|
|
*
|
|
* History:
|
|
* 12-03-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
PCURSOR _SetCursor(
|
|
PCURSOR pcur)
|
|
{
|
|
PQ pq;
|
|
PCURSOR pcurPrev;
|
|
|
|
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)
|
|
UpdateCursorImage();
|
|
}
|
|
|
|
return pcurPrev;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SetCursorPos (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 _SetCursorPos(
|
|
int x,
|
|
int y)
|
|
{
|
|
/*
|
|
* Blow it off if the caller doesn't have the proper access rights
|
|
*/
|
|
if (!CheckWinstaWriteAttributesAccess()) {
|
|
return FALSE;
|
|
}
|
|
|
|
InternalSetCursorPos(x, y, grpdeskRitInput);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InternalSetCursorPos
|
|
*
|
|
* 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 InternalSetCursorPos(
|
|
int x,
|
|
int y,
|
|
PDESKTOP pdesk)
|
|
{
|
|
gptCursorAsync.x = x;
|
|
gptCursorAsync.y = y;
|
|
|
|
BoundCursor();
|
|
|
|
ptCursor = gptCursorAsync;
|
|
|
|
GreMovePointer(gpDispInfo->hDev, ptCursor.x, ptCursor.y);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
SetFMouseMoved();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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--;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _ShowCursor (API)
|
|
*
|
|
* This API allows the application to hide or show the cursor image.
|
|
*
|
|
* History:
|
|
* 03-Dec-1990 JimA Implemented for fake cursor stuff
|
|
\***************************************************************************/
|
|
|
|
int _ShowCursor(
|
|
BOOL fShow)
|
|
{
|
|
PTHREADINFO pti = PtiCurrent();
|
|
PQ pq;
|
|
|
|
pq = pti->pq;
|
|
|
|
if (fShow) {
|
|
|
|
IncCursorLevel(pti);
|
|
|
|
if ((pq == gpqCursor) && (pq->iCursorLevel == 0))
|
|
UpdateCursorImage();
|
|
|
|
} else {
|
|
|
|
DecCursorLevel(pti);
|
|
|
|
if ((pq == gpqCursor) && (pq->iCursorLevel == -1))
|
|
UpdateCursorImage();
|
|
}
|
|
|
|
return pq->iCursorLevel;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _ClipCursor (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 _ClipCursor(
|
|
LPCRECT prcClip)
|
|
{
|
|
PEPROCESS Process = PsGetCurrentProcess();
|
|
|
|
/*
|
|
* Don't let this happen if it doesn't have access.
|
|
*/
|
|
if ( Process != gpepSystem &&
|
|
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(&rcCursorClip)) {
|
|
RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Access denied in _ClipCursor");
|
|
return FALSE;
|
|
}
|
|
|
|
if (prcClip == NULL) {
|
|
|
|
rcCursorClip = 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.
|
|
*/
|
|
rcCursorClip.left = max(gpDispInfo->rcScreen.left , prcClip->left);
|
|
rcCursorClip.right = min(gpDispInfo->rcScreen.right , prcClip->right);
|
|
rcCursorClip.top = max(gpDispInfo->rcScreen.top , prcClip->top);
|
|
rcCursorClip.bottom = min(gpDispInfo->rcScreen.bottom, prcClip->bottom);
|
|
|
|
/*
|
|
* Check for invalid clip rect.
|
|
*/
|
|
if (rcCursorClip.left > rcCursorClip.right ||
|
|
rcCursorClip.top > rcCursorClip.bottom) {
|
|
|
|
rcCursorClip = gpDispInfo->rcScreen;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update the cursor position if it's currently outside the
|
|
* cursor clip-rect.
|
|
*/
|
|
if (!PtInRect(&rcCursorClip, ptCursor))
|
|
InternalSetCursorPos(ptCursor.x, ptCursor.y, grpdeskRitInput);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _GetClipCursor (API)
|
|
*
|
|
* This API returns the cursor clipping rectangle which restricts where the
|
|
* cursor can go.
|
|
*
|
|
* History:
|
|
* 01-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
BOOL _GetClipCursor(
|
|
LPRECT prcClip)
|
|
{
|
|
/*
|
|
* Blow it off if the caller doesn't have the proper access rights
|
|
*/
|
|
RETURN_IF_ACCESS_DENIED(PpiCurrent()->amwinsta,
|
|
WINSTA_READATTRIBUTES,
|
|
FALSE);
|
|
|
|
*prcClip = rcCursorClip;
|
|
|
|
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(VOID)
|
|
{
|
|
if (gfVDMBoundsActive && gspwndFullScreen != NULL) {
|
|
|
|
if (gptCursorAsync.x < grcVDMCursorBounds.left) {
|
|
gptCursorAsync.x = grcVDMCursorBounds.left;
|
|
} else if (gptCursorAsync.x >= grcVDMCursorBounds.right) {
|
|
gptCursorAsync.x = grcVDMCursorBounds.right - 1;
|
|
}
|
|
|
|
if (gptCursorAsync.y < grcVDMCursorBounds.top) {
|
|
gptCursorAsync.y = grcVDMCursorBounds.top;
|
|
} else if (gptCursorAsync.y >= grcVDMCursorBounds.bottom) {
|
|
gptCursorAsync.y = grcVDMCursorBounds.bottom - 1;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (gptCursorAsync.x < rcCursorClip.left) {
|
|
gptCursorAsync.x = rcCursorClip.left;
|
|
} else if (gptCursorAsync.x >= rcCursorClip.right) {
|
|
gptCursorAsync.x = rcCursorClip.right - 1;
|
|
}
|
|
|
|
if (gptCursorAsync.y < rcCursorClip.top) {
|
|
gptCursorAsync.y = rcCursorClip.top;
|
|
} else if (gptCursorAsync.y >= rcCursorClip.bottom) {
|
|
gptCursorAsync.y = rcCursorClip.bottom - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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 gfVDMBoundsActive, because
|
|
* MoveEvent() calls BoundCursor() from outside the USER CritSect!
|
|
*/
|
|
grcVDMCursorBounds = *lprc;
|
|
gfVDMBoundsActive = TRUE;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Turn vdm bounds off.
|
|
*/
|
|
gfVDMBoundsActive = FALSE;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* AnimateCursor
|
|
*
|
|
* 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
|
|
|
|
LONG AnimateCursor(
|
|
PWND pwndDummy,
|
|
UINT message,
|
|
DWORD wParam,
|
|
LONG lParam)
|
|
{
|
|
int iicur;
|
|
PACON pacon;
|
|
int LostTime;
|
|
int tTime;
|
|
|
|
pacon = (PACON)gpcurLogCurrent;
|
|
|
|
if (pacon == NULL || !(pacon->CURSORF_flags & CURSORF_ACON)) {
|
|
gdwLastAniTick = 0;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
UpdateCursorImage();
|
|
|
|
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;
|
|
}
|
|
|
|
gdwLastAniTick = NtGetTickCount() - LostTime;
|
|
idCursorTimer = InternalSetTimer(NULL, idCursorTimer, tTime - LostTime, AnimateCursor, TMRF_RIT | TMRF_ONESHOT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined (_M_IX86) && (_MSC_VER <= 1100)
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
/**************************************************************************\
|
|
* UpdateCursorImage
|
|
*
|
|
* History:
|
|
* 14-Jan-1992 DavidPe Created.
|
|
* 09-Aug-1992 DarrinM Added animated cursor code.
|
|
\**************************************************************************/
|
|
|
|
VOID UpdateCursorImage(VOID)
|
|
{
|
|
PCURSOR pcurLogNew;
|
|
PCURSOR pcurPhysNew;
|
|
PACON pacon;
|
|
|
|
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" cursor.
|
|
*/
|
|
if (gtimeStartCursorHide != 0) {
|
|
|
|
if (gpqCursor->spcurCurrent == SYSCUR(ARROW) ||
|
|
gpqCursor->spcurCurrent == SYSCUR(APPSTARTING)) {
|
|
|
|
pcurLogNew = SYSCUR(APPSTARTING);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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, AnimateCursor, 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) {
|
|
|
|
gpcurPhysCurrent = pcurPhysNew;
|
|
|
|
if (pcurPhysNew == NULL) {
|
|
|
|
GreSetPointer(gpDispInfo->hDev, (PCURSINFO)NULL,0);
|
|
|
|
} else {
|
|
|
|
GreSetPointer(gpDispInfo->hDev,
|
|
(PCURSINFO)&(pcurPhysNew->xHotspot),
|
|
(pcurLogNew->CURSORF_flags & CURSORF_ACON) ? SPS_ANIMATEUPDATE : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* LockQCursor
|
|
*
|
|
* 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 LockQCursor(
|
|
PQ pq,
|
|
PCURSOR pcur)
|
|
{
|
|
PCURSOR pcurPrev;
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* See if the queue is marked for destuction. If so, we should not
|
|
* be trying to lock a cursor.
|
|
*/
|
|
if (pq->QF_flags & QF_INDESTROY) {
|
|
KdPrint(("LockQCursor: Attempting to lock cursor to freed queue\n"));
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
pcurPrev = Lock(&pq->spcurCurrent, pcur);
|
|
pq->hcurCurrent = (HCURSOR)PtoH(pcur);
|
|
return(pcurPrev);
|
|
}
|