/********************************************************************
*   DrvCursor.c
*
*   Macintosh device specific cursor sub-system implementation.
*
*   Copyright (c) 1994-1997, Willows Software Inc.  All rights reserved.
********************************************************************/

#include "stdio.h"

#include "DrvHook.h"
#include "Log.h"
#include "DrvDP.h"
#include "DrvWindows.h"
#include "DrvGlobals.h"
#include "DrvEvents.h"
#include "DrvKeyboard.h"

/* Exported functions */
void DrvUpdateCursor(BOOL inWindow);
long CursorMoved(EventRecord *event);

/* Local prototypes */
static DWORD DrvGetCursorPos(LPPOINT lppt);
static DWORD DoCursorEvent(EventRecord *event);

/* Local variables */
static int gIsSysCursor = TRUE;		/* If cursor has been set to a window's cursor */
static Cursor *gWindowCursor = &(qd.arrow);		/* Initialize with the arrow cursor */
static gCursorHidden = 0;					/* Track cursor hidden levels */
static Point gOldPt;							/* Mouse movement */

/********************************************************************
*   PrivateCursorHook
*
********************************************************************/
DWORD PrivateCursorHook(WORD wFunc, LPARAM dwParam1, LPARAM dwParam2, LPVOID lpStruct)
{
#pragma unused (dwParam2)

    switch (wFunc) {
	case DSUBSYSTEM_INIT:
	case DSUBSYSTEM_GETCAPS:
	    return 1L;
	    
	case DSUBSYSTEM_EVENTS:
		return DoCursorEvent(lpStruct);
		
	case PCH_SETCURSOR:
		/* The cursor has already been created in DrvImages, so just set it */
		if (dwParam1) {
			gWindowCursor = (Cursor *) dwParam1;
			if (!gInBackground) {
				SetCursor((Cursor *) dwParam1);		/* This is overriden when in bkgrnd */
				gIsSysCursor = FALSE;							/* Therefore, do not set flag */

				/* TEMPORARY to account for the libraries lack of proper depth control. */
				while (gCursorHidden > 0) {
					ShowCursor();				
					gCursorHidden--;
				}
			}
		}
	    return 1L;

	case PCH_GETCURSORPOS:
		return DrvGetCursorPos((LPPOINT)lpStruct);

	case PCH_SETCURSORPOS:
	    return FALSE;				/* We should not, and is difficult to, change cursor position */

	case PCH_SETCLIPCURSOR:
	    return FALSE;

	case PCH_GETCLIPCURSOR:
	    return FALSE;

	case PCH_SHOWCURSOR:
	    /* driver should return TRUE if it handles this case */
		ShowCursor();
		if (gCursorHidden > 0)
			gCursorHidden--;
		return TRUE;
		
	case PCH_HIDECURSOR:
	    /* driver should return TRUE if it handles this case */
#if LATER
		//There is a bug in the library and since the x driver does not support
		// cursor hiding, we cannot either for now.
		// The bug is with solitaire, select a menu and the cursor is hidden by solitaire.
		//  But, the menu manager does not set the cursor and thus is stays hidden!
	   	HideCursor();
#else
		// Not hiding has the side effect of leaving cursor in the wrong state, so force an update for now.
		gOldPt.h = -32000;				/* Force a mouse moved event to update cursors */
#endif
	   	gCursorHidden++;
	    return TRUE;

	default:
	    return 0L;

    }
}


/********************************************************************
*   CursorMoved
*
********************************************************************/
long CursorMoved(EventRecord *event) 
{
Point curPt;
GrafPtr wMgrPort;

	/* Track the mouse movements */
	GetWMgrPort(&wMgrPort);		/* Use the window manager port since who knows which one (if any) are set */
	SetPort(wMgrPort);
	GetMouse(&curPt);
	
	if (*(long *)&curPt != *(long *)&gOldPt) {		/* Compare longs to avoid toolbox call */
		event->what = APP_EVT;								/* Use this for mouseMoved events */
		event->message = EVT_MOUSEMOVE;			/* No messages for mouse events */
		event->where = curPt;
		event->when = TickCount();							/* Latch the current time */
		gOldPt = curPt;												/* Latch current position */

		event->modifiers = 0;									/* Clear the modifiers, we will add the bits */

		if (StillDown()) {
			event->modifiers |= btnState;					/* Set button state */

			if (IsKeyPressed(AK_Command))
				event->modifiers |= cmdKey;
			if (IsKeyPressed(AK_Option))
				event->modifiers |= optionKey;
		}
		
		return(true);
	}
	else
		return(false);
		
}



/********************************************************************
*   DrvGetCursorPos
*
*	Gets current curs position relative to root window.
********************************************************************/
static DWORD DrvGetCursorPos(LPPOINT lppt)
{
GrafPtr wMgrPort, savePort;
Point curPt;

	/* use the window manager (root) window */
	GetPort(&savePort);					/* Be cooperative */
	GetWMgrPort(&wMgrPort);		
	SetPort(wMgrPort);
	GetMouse(&curPt);						/* Current location relative to current port */

	lppt->x = curPt.h;
	lppt->y = curPt.v;

	SetPort(savePort);
	
	return(TRUE);
}


/********************************************************************
*   DrvUpdateCursor
*
*	Updates the cursor if it is over a window or not.
********************************************************************/
void DrvUpdateCursor(BOOL inWindow)
{
	/* Set the window cursor only when it is not already set. */
	if (inWindow && gIsSysCursor && !gInBackground) {
		SetCursor(gWindowCursor);
		gIsSysCursor = FALSE;
	}
	
	/* Set system cursor when no longer in a window and cursor is still set to the window */
	if (!inWindow && !gIsSysCursor) {
		SetCursor(&qd.arrow);
		gIsSysCursor = TRUE;
	}
}

/********************************************************************
*   DoCursorEvent
*
*	Handle cursor events.
********************************************************************/
static DWORD DoCursorEvent(EventRecord *event)
{

	switch(event->what) {
	case APP_EVT:
		switch (event->message) {
		case EVT_SUSPENDAPP:
			/* We are being switched out, be cooperative and reset cursor appropriately */
			if (gCursorHidden) {
				gCursorHidden = 0;
				InitCursor();						/* Reset all levels of hiding */
				gIsSysCursor = TRUE;
			}
			break;
			
		case EVT_RESUMEAPP:
			gOldPt.h = -32000;				/* Force a mouse moved event to update cursors */
			break;

		default:
			break;
		}	
		return 0;
		
	default:
	    return 0L;

	}

}
