// --------------------------------------------------------------------------
//
//  Mag_Hook.cpp
//
//  Accessibility Event trapper for magnifier. Uses an out-of-context WinEvent
//  hook (if MSAA is installed) or a mouse hook (if MSAA is not installed) to 
//  tell the app where to magnify.
//
//  Mainly what we want this to do is to watch for focus changes, caret
//  movement, and mouse pointer movement, and then post the appropriate
//  location to the Magnifier app so it can magnify the correct area.
//
// --------------------------------------------------------------------------
#define STRICT

#include <windows.h>
#include <windowsx.h>

// When building with VC5, we need winable.h since the active
// accessibility structures are not in VC5's winuser.h.  winable.h can
// be found in the active accessibility SDK
#ifdef VC5_BUILD___NOT_NT_BUILD_ENVIRONMENT
#include <winable.h>
#else
// The Active Accessibility SDK used WINABLEAPI for the functions.  When
// the functions were moved to winuser.h, WINABLEAPI was replaced with WINUSERAPI.
#define WINABLEAPI WINUSERAPI
#endif

#include <ole2.h>
#include <oleacc.h>

#define MAGHOOKAPI  __declspec(dllexport)
#include "Mag_Hook.h"
#include <math.h>

#include "wineventrefilter.h"
#include "w95trace.c"
#include "mappedfile.cpp"

BOOL TryFindCaret( HWND hWnd, IAccessible * pAcc, VARIANT * pvarChild, RECT * prc );
BOOL IsFocussedItem( HWND hWnd, IAccessible * pAcc, VARIANT varChild );


// --------------------------------------------------------------------------
//
// Definitions so we don't have to statically link to OLEACC.DLL
// 
// We need the following three functions that were in the Active Accessibility SDK
//
// STDAPI AccessibleObjectFromEvent(HWND hwnd, DWORD dwId, DWORD dwChildId, IAccessible** ppacc, VARIANT* pvarChild);
// WINABLEAPI HWINEVENTHOOK WINAPI SetWinEventHook(DWORD eventMin, DWORD eventMax, HMODULE hmodWinEventProc, WINEVENTPROC lpfnWinEventProc, DWORD idProcess, DWORD idThread, DWORD dwFlags);
// WINABLEAPI BOOL WINAPI UnhookWinEvent(HWINEVENTHOOK hEvent);
//
// --------------------------------------------------------------------------
typedef HRESULT (_stdcall *_tagAccessibleObjectFromEvent)(HWND hwnd, DWORD dwId, DWORD dwChildId, IAccessible** ppacc, VARIANT* pvarChild);
typedef WINABLEAPI HWINEVENTHOOK (WINAPI *_tagSetWinEventHook)(DWORD eventMin, DWORD eventMax, HMODULE hmodWinEventProc, WINEVENTPROC lpfnWinEventProc, DWORD idProcess, DWORD idThread, DWORD dwFlags);
typedef WINABLEAPI BOOL (WINAPI *_tagUnhookWinEvent)(HWINEVENTHOOK hEvent);

_tagAccessibleObjectFromEvent pAccessibleObjectFromEvent = NULL;
_tagSetWinEventHook pSetWinEventHook = NULL;
_tagUnhookWinEvent pUnhookWinEvent = NULL;


// Workaround for menus - menus 'steal' focus, and don't hand it back
// - so we have to remember where it was before going into menu mode, so we
// can restore it properly afterwards.
POINT g_ptLastKnownBeforeMenu;
BOOL g_InMenu = FALSE;
RECT g_rcMenu = {0, 0, 0, 0};

SIZE  g_ZoomSz;

// --------------------------------------------------------------------------
//
//  GetAcctiveAccessibleFunctions()
//
//  This function attempts to load the active accessibility functions we need
//  from OLEACC.DLL and USER32.DLL
//
//  If the functions are availible, this returns TRUE
//
// --------------------------------------------------------------------------
BOOL GetAcctiveAccessibleFunctions()
{
	HMODULE hOleAcc = NULL;
	HMODULE hUser;
	if(!(hOleAcc = LoadLibrary(__TEXT("oleacc.dll"))))
		return FALSE;
	if(!(pAccessibleObjectFromEvent = (_tagAccessibleObjectFromEvent)GetProcAddress(hOleAcc, "AccessibleObjectFromEvent")))
		return FALSE;
	if(!(hUser = GetModuleHandle(__TEXT("user32.dll"))))
		return FALSE;
	if(!(pSetWinEventHook = (_tagSetWinEventHook)GetProcAddress(hUser, "SetWinEventHook")))
		return FALSE;
	if(!(pUnhookWinEvent = (_tagUnhookWinEvent)GetProcAddress(hUser, "UnhookWinEvent")))
		return FALSE;
	return TRUE;
};


// --------------------------------------------------------------------------
//
// Per-process Variables
//
// --------------------------------------------------------------------------
#ifndef ARRAYSIZE
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
#endif

HMODULE     g_hModEventDll;
UINT        g_auiEvents[] = {
                  EVENT_OBJECT_FOCUS
                , EVENT_OBJECT_LOCATIONCHANGE
                , EVENT_SYSTEM_MENUSTART
                , EVENT_SYSTEM_MENUPOPUPSTART
                , EVENT_SYSTEM_MENUEND
                , EVENT_SYSTEM_DIALOGEND
                };

// --------------------------------------------------------------------------
//
// Shared Variables are in the GLOBALDATA structure
//
// --------------------------------------------------------------------------
struct GLOBALDATA {
    UINT            g_cEvents;
    HWINEVENTHOOK	g_hEventHook[ARRAYSIZE(g_auiEvents)];
    HHOOK			g_hMouseHook;
    HWND			g_hwndEventPost;
    DWORD_PTR		g_dwCursorHack;
    DWORD_PTR		g_MainExeThreadID;
};

// pointer to shared global data
GLOBALDATA *g_pGlobalData = 0;
      
// pointer to mem mapped file handle
CMemMappedFile *g_CMappedFile = 0;                       
                    
// size of global data memory mapped file
const int c_cbGlobalData =  sizeof(GLOBALDATA);
          
// name of memory mapped file
const TCHAR c_szMappedFileName[] = TEXT("MagnifyShared");

// mutex to access mem mapped file and wait time
const TCHAR c_szMutexMagnify[] = TEXT("MagnifyMutex");
const int c_nMutexWait = 5000;

BOOL CreateMappedFile()
{
    g_CMappedFile = new CMemMappedFile;
    if (g_CMappedFile)
    {
        if (g_CMappedFile->Open(c_szMappedFileName, c_cbGlobalData))
        {
            CScopeMutex csMutex;
            if (csMutex.Create(c_szMutexMagnify, c_nMutexWait))
            {
                g_CMappedFile->AccessMem((void **)&g_pGlobalData);
                if (g_CMappedFile->FirstOpen())
                {
                    memset(g_pGlobalData, 0, c_cbGlobalData);
                }
                return TRUE;
            }
        }
    }

    // ISSUE Is it possible to see this error when switching desktops and
    // UtilMan is controlling magnify if it can't gracefully shut down the
    // previous version before starting up one on the new desktop?  Could
    // work around this if its a problem by prefixing the name w/desktop.

    return FALSE;
}
void CloseMappedFile()
{
    if (g_CMappedFile)
    {
        g_CMappedFile->Close();
        delete g_CMappedFile;
        g_CMappedFile = 0;
    }
}

// --------------------------------------------------------------------------
//
// Functions Prototypes (Foward References of callback functions
//
// --------------------------------------------------------------------------
void CALLBACK NotifyProc(HWINEVENTHOOK hEvent, DWORD event, HWND hwndMsg, LONG idObject,
						LONG idChild, DWORD idThread, DWORD dwEventTime);

LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);


CWinEventReentrancyFilter< NotifyProc > * g_pWinEventReFilter;


// --------------------------------------------------------------------------
//
//  DllMain()
//
// --------------------------------------------------------------------------
BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID fImpLoad)
{
    switch (dwReason) {

		case DLL_PROCESS_ATTACH:
			g_hModEventDll = hInst;

            // Create the memory mapped file for shared global data
            CreateMappedFile();

            break;

		case DLL_PROCESS_DETACH:
            // Close the memory mapped file for shared global data
            CloseMappedFile();

            break;
    }

    return(TRUE);
}

// --------------------------------------------------------------------------
//
//  InstallEventHookWithOleAcc
//
//  This installs the WinEvent hook if hwndPostTo is not null, or removes the
//  hook if the parameter is null. Does no checking for a valid window handle.
//
//  If successful, this returns TRUE.
//
// --------------------------------------------------------------------------
BOOL WINAPI InstallEventHookWithOleAcc (HWND hwndPostTo) 
{
	if (hwndPostTo != NULL) 
    {
		g_pGlobalData->g_MainExeThreadID = GetCurrentThreadId();

		if(g_pGlobalData->g_hwndEventPost || g_pGlobalData->g_cEvents)
			return FALSE; // We already have hooks installed - you can only have one at a time

		// Install the hook
		g_pGlobalData->g_hwndEventPost = hwndPostTo; // Must set this before installing the hook

        g_pWinEventReFilter = new CWinEventReentrancyFilter< NotifyProc >;
        if( ! g_pWinEventReFilter )
        {
            return FALSE;
        }

        // Only hook the events we care about. If we capture all events it degrades
        // the machine if there are lots of windows open at one time.  In particular
        // consoles send many events and hooking these can quickly overrun the console
        // input thread during high activity.  Note that if some of the SetWinEventHook
        // calls fail the global handle array no longer corresponds one-to-one with the
        // event constant array.  We take that into consideration when unhooking these.

        {
            int cEvents = ARRAYSIZE(g_auiEvents);
            for (int i=0;i<cEvents;i++)
            {
		        // Currently using a OUTOFCONTEXT hook. As the INCONTEXT  
		        // hook is not going to work if launched from UM. 
		        g_pGlobalData->g_hEventHook[g_pGlobalData->g_cEvents] = pSetWinEventHook(
                                                                            g_auiEvents[i], 
                                                                            g_auiEvents[i], 
                                                                            g_hModEventDll,
                                                                            g_pWinEventReFilter->WinEventProc,
                                                                            0, 
                                                                            0, 
                                                                            WINEVENT_OUTOFCONTEXT);
                if (g_pGlobalData->g_hEventHook[g_pGlobalData->g_cEvents])
                {
                    g_pGlobalData->g_cEvents++;
                }
            }
        }

		if (!g_pGlobalData->g_cEvents) 
        {
			// Something went wrong - reset g_pGlobalData->g_hwndEventPost to NULL
			g_pGlobalData->g_hwndEventPost = NULL;

			return FALSE;
		}
	} 
    else 
    {
		// NOTE - We never fail if they are trying to uninstall the hook
		g_pGlobalData->g_hwndEventPost = NULL;
		// Uninstalling the hooks
        for (int i = 0; i < g_pGlobalData->g_cEvents; i++)
        {
			BOOL fRv = pUnhookWinEvent(g_pGlobalData->g_hEventHook[i]);
			g_pGlobalData->g_hEventHook[i] = NULL;
        }
	
        delete g_pWinEventReFilter;
        g_pWinEventReFilter = NULL;
    }
	return TRUE;
}

// --------------------------------------------------------------------------
//
//  InstallEventHookWithoutOleAcc
//
//  This installs a mouse hook so we have some functionality when oleacc is
//  not installed
//
//  If successful, this returns TRUE.
//
// --------------------------------------------------------------------------
BOOL WINAPI InstallEventHookWithoutOleAcc (HWND hwndPostTo) 
{
	if (hwndPostTo != NULL) 
    {
		if(g_pGlobalData->g_hwndEventPost || g_pGlobalData->g_hMouseHook)
			return FALSE; // We already have a hook installed - yo u can only have one at a time

		// Install the hook
		g_pGlobalData->g_hwndEventPost = hwndPostTo; // Must set this before installing the hook
		g_pGlobalData->g_hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, g_hModEventDll, 0);
		if (!g_pGlobalData->g_hMouseHook) 
        {
			// Something went wrong - reset g_pGlobalData->g_hwndEventPost to NULL
			g_pGlobalData->g_hwndEventPost = NULL;
			return FALSE;
		}
	} 
    else 
    {
		// NOTE - We never fail if they are trying to uninstall the hook
		g_pGlobalData->g_hwndEventPost = NULL;
		// Uninstalling the hook
		if (g_pGlobalData->g_hMouseHook != NULL) 
        {
			UnhookWindowsHookEx(g_pGlobalData->g_hMouseHook);
			g_pGlobalData->g_hMouseHook = NULL;
		}
	}
	return TRUE;
}


// --------------------------------------------------------------------------
//
//  InstallEventHook
//
//  This function checks to see if Ole Accessibility is installed, and if so
//  uses the WinEvent hook.  Otherwise, it uses a mouse hook.
//
//  If successful, this returns TRUE.
//
// --------------------------------------------------------------------------

BOOL g_bOleAccInstalled = FALSE;
BOOL g_bCheckOnlyOnceForOleAcc = TRUE;

BOOL WINAPI InstallEventHook (HWND hwndPostTo) 
{
    CScopeMutex csMutex;
    if (!csMutex.Create(c_szMutexMagnify, c_nMutexWait))
    {
        DBPRINTF(TEXT("InstallEventHook:  CScopeMutex::Create FAILED!\r\n"));
        return FALSE;
    }

	// We check onl y the first time if Ole Acc is installed.  From then on,
	// we assume it remains constant.
	if(g_bCheckOnlyOnceForOleAcc) 
    {
		g_bCheckOnlyOnceForOleAcc = FALSE;
		g_bOleAccInstalled = GetAcctiveAccessibleFunctions();
	}

	if(g_bOleAccInstalled)
		return InstallEventHookWithOleAcc(hwndPostTo);
	else
		return InstallEventHookWithoutOleAcc(hwndPostTo);
}

// --------------------------------------------------------------------------
//
//  GetCursorHack()
//
//  This function returns the last known user cursor handle.
//  
// --------------------------------------------------------------------------

DWORD_PTR WINAPI GetCursorHack()
{
    CScopeMutex csMutex;
    if (!csMutex.Create(c_szMutexMagnify, c_nMutexWait))
    {
        DBPRINTF(TEXT("GetCursorHack:  CScopeMutex::Create FAILED!\r\n"));
        return NULL;
    }

	return g_pGlobalData->g_dwCursorHack;
}

WPARAM CalcZoom(RECT &rcLoc, BOOL fCenter)
{
	int ZoomX;
	int ZoomY;

	// If the focussed object does not fit into the zoom area, 
	// Then reset the location of the focussed rectangle
	// BUG: Need to handle RTL languages

	// Does Loc rect fit horizontally?
	if( g_ZoomSz.cx <= abs(rcLoc.left - rcLoc.right) )
	{
		// it doesn't so left justify 
		ZoomX = rcLoc.left + (g_ZoomSz.cx/2);
	}
	else
	{
		// it fits so center 
		ZoomX = (rcLoc.left + rcLoc.right) / 2;
	}

    if (fCenter)
    {
	    // Does Loc rect fit vertically?
	    if( g_ZoomSz.cy <= abs(rcLoc.top - rcLoc.bottom) )
	    {
		    // it doesn't so center near top
		    ZoomY = rcLoc.top + (g_ZoomSz.cy/2);
	    }
	    else
	    {
		    // it fits so center 
		    ZoomY = (rcLoc.top + rcLoc.bottom) / 2;
	    }
    } else
    {
        // Don't center vertically; focus at the top
        ZoomY = rcLoc.top;
    }

	return MAKELONG( ZoomX, ZoomY );
}

// --------------------------------------------------------------------------
//
//  NotifyProc()
//
//	This is the callback function for the WinEvent Hook we install. This
//	gets called whenever there is an event to process. The only things we
//	care about are focus changes and mouse/caret movement. The way we handle
//  the events is to post a message to the client (ScreenX) telling it
//	where the focus/mouse/caret is right now. It can then decide where it 
//	should be magnifying.
//
//	Parameters:
//		hEvent			A handle specific to this call back
//		event			The event being sent
//		hwnd			Window handle of the window generating the event or 
//						NULL if no window is associated with the event.
//		idObject		The object identifier or OBJID_WINDOW.
//		idChild			The child ID of the element triggering the event, 
//						or CHILDID_SELF if the event is for the object itself.
//		dwThreadId		The thread ID of the thread generating the event.  
//						Informational only.
//		dwmsEventTime	The time of the event in milliseconds.
// --------------------------------------------------------------------------
/* Forward ref */ BOOL GetObjectLocation(IAccessible * pacc, VARIANT* pvarChild, LPRECT lpRect);
void CALLBACK NotifyProc(HWINEVENTHOOK hEvent, DWORD event, HWND hwndMsg, LONG idObject,
						 LONG idChild, DWORD idThread, DWORD dwmsEventTime) 
{

	WPARAM			wParam;
	LPARAM			lParam;
	// Initialize pac to NULL so that we Release() pointers we've obtained.
	// Otherwise we will continually leak a heck of memory.
	IAccessible *	pacc = NULL;
	RECT			LocationRect, CaretSrch;
	VARIANT 		varChild;
	HRESULT 		hr;
	BOOL			bX, bY;

    CScopeMutex csMutex;
    if (!csMutex.Create(c_szMutexMagnify, c_nMutexWait))
    {
        DBPRINTF(TEXT("NotifyProc:  CScopeMutex::Create FAILED!\r\n"));
        return;
    }

	switch (event) 
    {
	    case EVENT_OBJECT_FOCUS:
		    hr = pAccessibleObjectFromEvent(hwndMsg, idObject, idChild, &pacc, &varChild);
		    if (!SUCCEEDED(hr))
            {
                DBPRINTF(TEXT("NotifyProc(EVENT_OBJECT_FOCUS): AccessibleObjectFromEvent failed 0x%x\r\n"), hr);
			    return;
            }
		    if (!GetObjectLocation(pacc,&varChild,&LocationRect))
            {
                DBPRINTF(TEXT("NotifyProc(EVENT_OBJECT_FOCUS): GetObjectLocation failed\r\n")); 
			    break;
            }
            // Got object rect - fall through and continue processing...

			// Ignore bogus focus events
			if ( !IsFocussedItem( hwndMsg, pacc, varChild ) )
            {
                DBPRINTF(TEXT("NotifyProc(EVENT_OBJECT_FOCUS): Ignoring event:  state is not Focused\r\n"));
				return;
            }

			// Remove bogus all zero events from IE5.0
			if ( (LocationRect.top == 0) && (LocationRect.left == 0) && 
				  (LocationRect.bottom == 0) && (LocationRect.right == 0))
            {
                DBPRINTF(TEXT("NotifyProc(EVENT_OBJECT_FOCUS):  Ignoring location {0,0,0,0}\r\n"));
				return;
            }

			wParam = CalcZoom(LocationRect, TRUE);
		
            // Only update 'last known non-menu point' for focus while not in menu mode
            if( !g_InMenu )
            {
                g_ptLastKnownBeforeMenu.x = LOWORD( wParam );
                g_ptLastKnownBeforeMenu.y = HIWORD( wParam );
            }

		    // JMC: TODO: Make sure the top left corner of the object is in the zoom rect
			// BMCK: PostMessage->SendMessage, since we're in-context. (Avoids hogging message queue)
		    SendMessage(g_pGlobalData->g_hwndEventPost, WM_EVENT_FOCUSMOVE, wParam, 0);
            // OutputDebugString(TEXT(" - success\r\n"));
		    break;
		    
	    case EVENT_OBJECT_LOCATIONCHANGE:
		    switch (idObject) 
            {
		        case OBJID_CARET:
			        hr = pAccessibleObjectFromEvent (hwndMsg,idObject,idChild, &pacc, &varChild);
			        if (!SUCCEEDED(hr))
                    {
                        DBPRINTF(TEXT("NotifyProc(EVENT_OBJECT_LOCATIONCHANGE):  AccessibleObjectFromEvent FAILED 0x%x\r\n"), hr);
				        return;
                    }
			        if (!GetObjectLocation (pacc,&varChild,&LocationRect))
                    {
                        DBPRINTF(TEXT("NotifyProc(EVENT_OBJECT_LOCATIONCHANGE):  GetObjectLocation FAILED\r\n"));
				        break;
                    }
			        
			        // center zoomed area on center of focus rect.
			        wParam = MAKELONG(((LocationRect.left + LocationRect.right) / 2), ((LocationRect.bottom + LocationRect.top) / 2));
			        lParam = dwmsEventTime;

                    // Only update 'last known non-menu point' for caret while not in menu mode
                    if( !g_InMenu )
                    {
                        g_ptLastKnownBeforeMenu.x = LOWORD( wParam );
                        g_ptLastKnownBeforeMenu.y = HIWORD( wParam );
                    }

			        // BMCK: PostMessage->SendMessage, since we're in-context. (Avoids hogging message queue)
			        SendMessage(g_pGlobalData->g_hwndEventPost, WM_EVENT_CARETMOVE, wParam, lParam);
			        break;
			        
		        case OBJID_CURSOR:
			        hr = pAccessibleObjectFromEvent (hwndMsg,idObject,idChild, &pacc, &varChild);
			        if (!SUCCEEDED(hr))
                    {
                        DBPRINTF(TEXT("NotifyProc(OBJID_CURSOR):  AccessibleObjectFromEvent FAILED 0x%x\r\n"), hr);
				        return;
                    }
			        if (!GetObjectLocation (pacc,&varChild,&LocationRect))
                    {
                        DBPRINTF(TEXT("NotifyProc(OBJID_CURSOR):  GetObjectLocation FAILED\r\n"));
				        break;
                    }
			        wParam = MAKELONG(LocationRect.left, LocationRect.top);
			        lParam = dwmsEventTime;

                    // update 'last known non-menu point' for mouse even if in menu mode -
                    // mouse moves can occur while in menu mode, and we do want to remember them.
                    g_ptLastKnownBeforeMenu.x = LOWORD( wParam );
                    g_ptLastKnownBeforeMenu.y = HIWORD( wParam );

			        // BMCK: PostMessage->SendMessage, since we're in-context. (Avoids hogging message queue)
                    SendMessage(g_pGlobalData->g_hwndEventPost, WM_EVENT_MOUSEMOVE, wParam, lParam);
			        break;
		    }
		    break;

            case EVENT_SYSTEM_MENUSTART:
				// Set flag indicating we are in pop-up menu only
				// if the event came from the magnify window
				if (hwndMsg == g_pGlobalData->g_hwndEventPost)
				{
					g_InMenu = TRUE;
				}
                break;

				// Fix context menu tracking. :a-anilk
			case EVENT_SYSTEM_MENUPOPUPSTART:
				{
					TCHAR buffer[100];

					GetClassName(hwndMsg,buffer,100); 

					hr = pAccessibleObjectFromEvent (hwndMsg,idObject,idChild, &pacc, &varChild);
					if (!SUCCEEDED(hr))
                    {
                        DBPRINTF(TEXT("NotifyProc(EVENT_SYSTEM_MENUPOPUPSTART):  AccessibleObjectFromEvent FAILED 0x%x\r\n"), hr);
				        return;
                    }
					if (!GetObjectLocation (pacc,&varChild,&LocationRect))
                    {
                        DBPRINTF(TEXT("NotifyProc(EVENT_SYSTEM_MENUPOPUPSTART):  GetObjectLocation FAILED\r\n"));
				        break;
                    }

                    // if we are in the popup menu then save its window location info
                    // so that the main mag window can bitblt it with the cursor

					lParam = dwmsEventTime;
                    if (!g_InMenu)
                    {
					    wParam = CalcZoom(LocationRect, FALSE);
                    }
                    else
                    {
                        IAccessible * paccParent;
                        hr = pacc->get_accParent((IDispatch **)&paccParent);
                        if (SUCCEEDED(hr))
                        {
                            VARIANT varSelf;
                            varSelf.vt = VT_I4;
                            varSelf.lVal = CHILDID_SELF;
                            GetObjectLocation(paccParent, &varSelf, &g_rcMenu);
                            paccParent->Release();
                        }
                        else
                        {
                            g_rcMenu = LocationRect;
                        }
					    wParam = CalcZoom(g_rcMenu, FALSE);
                    }

                    SendMessage(g_pGlobalData->g_hwndEventPost, WM_EVENT_FORCEMOVE, wParam, lParam);
				}
				break;
	
            case EVENT_SYSTEM_MENUEND:
                if(g_InMenu )
                {
                    g_InMenu = FALSE;
                    memset(&g_rcMenu, 0, sizeof(RECT));

			        lParam = GetTickCount();
                    wParam = MAKELONG( g_ptLastKnownBeforeMenu.x,
                                       g_ptLastKnownBeforeMenu.y );;

        			// BMCK: PostMessage->SendMessage, since we're in-context. (Avoids hogging message queue)
                    SendMessage(g_pGlobalData->g_hwndEventPost, WM_EVENT_FORCEMOVE, wParam, lParam);
                }
                break;

            case EVENT_SYSTEM_DIALOGEND:
                break;
	}
	if (pacc)
		pacc->Release();
}

// --------------------------------------------------------------------------
//
//	GetObjectLocation()
//
//	This fills in a RECT that has the location of the Accessible object
//	specified by pacc and idChild. The coordinates returned are screen
//	coordinates.
//
// --------------------------------------------------------------------------
BOOL GetObjectLocation(IAccessible * pacc, VARIANT* pvarChild, LPRECT lpRect) 
{
	HRESULT hr;
	SetRectEmpty(lpRect);
	
	hr = pacc->accLocation(&lpRect->left, &lpRect->top, &lpRect->right, &lpRect->bottom, *pvarChild);
	
	// the location is not a rect, but a top left, plus a width and height.
	// I want it as a real rect, so I'll convert it.
	lpRect->right  = lpRect->left + lpRect->right;
	lpRect->bottom = lpRect->top  + lpRect->bottom;
	
	if ( hr != S_OK )
    {
		return(FALSE);
    }

	return(TRUE);
}



// --------------------------------------------------------------------------
//
//  MouseProc()
//
//	This is the callback function for the Mouse Hook we install.
//
//	Parameters:
// --------------------------------------------------------------------------
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam)
{
    CScopeMutex csMutex;
    if (csMutex.Create(c_szMutexMagnify, c_nMutexWait))
    {
        DBPRINTF(TEXT("MouseProc:  CScopeMutex::Create FAILED!\r\n"));
        return 0;   // TODO not sure what value to return; MSDN is unclear
    }

	// For WM_MOUSEMOVE and WM_NCMOUSEMOVE messages, we post the main window
	// WM_EVENT_MOUSEMOVE messages.  We don't want to do this if we are in
	// the address space of MAGNIFY.EXE.  To avoid this, we also check that
	// g_bCheckOnlyOnceForOleAcc is TRUE.  If g_bCheckOnlyOnceForOleAcc is TRUE,
	// we are in another processes address space.
	// If we posted ourselves WM_EVENT_MOUSEMOVE while in MAGNIFY.EXE, we got all
	// sorts of weird crashes.
	if((WM_MOUSEMOVE == wParam || WM_NCMOUSEMOVE == wParam) && g_bCheckOnlyOnceForOleAcc)
	{
		g_pGlobalData->g_dwCursorHack = (DWORD_PTR)GetCursor(); // JMC: Hack to get cursor on systems that don't support new GetCursorInfo
		MOUSEHOOKSTRUCT *pmhs = (MOUSEHOOKSTRUCT *)lParam;
		PostMessage(g_pGlobalData->g_hwndEventPost, WM_EVENT_MOUSEMOVE, MAKELONG(pmhs->pt.x, pmhs->pt.y), 0);
	}

	return CallNextHookEx(g_pGlobalData->g_hMouseHook, code, wParam, lParam);
}

// --------------------------------------------------------------------------
//
// FakeCursorMove
//
// This function is called to 'fake' the cursor moving.  It is used by the
// magnifier app when a MouseProc is used.  We run into problems when
// posting ourselves messages from the MouseProc of our own process.  To
// avoid these problems, MouseProc() does not post a WM_EVENT_MOUSEMOUVE
// if we are in the address space of MAGNIFY.EXE.  Instead, MAGNIFY.EXE
// is responsible for calling FakeCursorMove() whenever the mouse moves over
// a window of its own process. (NOTE: This is really easy to accomplish in
// MFC.  We just call FakeCursorMove() from PreTranslateMessage() - see
// MagBar.cpp and MagDlg.cpp
//
// --------------------------------------------------------------------------

void WINAPI FakeCursorMove(POINT pt)
{
    CScopeMutex csMutex;
    if (!csMutex.Create(c_szMutexMagnify, c_nMutexWait))
    {
        DBPRINTF(TEXT("FakeCursorMove:  CScopeMutex::Create FAILED!\r\n"));
        return;
    }

	g_pGlobalData->g_dwCursorHack = (DWORD_PTR)GetCursor(); // JMC: Hack to get cursor on systems that don't support new GetCursorInfo
	PostMessage(g_pGlobalData->g_hwndEventPost, WM_EVENT_MOUSEMOVE, MAKELONG(pt.x, pt.y), 0);
}

// --------------------------------------------------------------------------
//
// SetZoomRect: Sets the maximum zoom rectangle.
//
// --------------------------------------------------------------------------

void SetZoomRect( SIZE sz )
{
	g_ZoomSz = sz;
}

// --------------------------------------------------------------------------
//
// GetPopupInfo: returns the rect of the menu popup in screen coordinates
//               or zero'd rect if menu popup isn't up.
//
// --------------------------------------------------------------------------
// TODO do all this with a little class
void GetPopupInfo(RECT *prect)
{
    if (g_InMenu)
    {
        *prect = g_rcMenu;
    } else
    {
        memset(prect, 0, sizeof(RECT));
    }
}

BOOL TryFindCaret( HWND hWnd, IAccessible * pAcc, VARIANT * pvarChild, RECT * prc )
{
    // Check that it is the currently active caret...
    GUITHREADINFO gui;
	TCHAR buffer[100];

	GetClassName(hWnd,buffer,100); 

    gui.cbSize = sizeof(GUITHREADINFO);
    if( ! GetGUIThreadInfo( NULL , &gui ) )
    {
        OutputDebugString( TEXT("GetGUIThreadInfo failed") );
        return FALSE;
    }
        
    if( gui.hwndCaret != hWnd )
    {
        return FALSE;
    }

	// Is it toolbar, We cannot determine who had focus!!!
	if ( (lstrcmpi(buffer, TEXT("ToolbarWindow32")) == 0) ||
		(lstrcmpi(buffer, TEXT("Internet Explorer_Server")) == 0))
			MessageBeep(100);
			// return FALSE;

    // Try to get the caret for that window (if one exists)...
    IAccessible * pAccCaret = NULL;
    VARIANT varCaret;
    varCaret.vt = VT_I4;
    varCaret.lVal = CHILDID_SELF;
    if( S_OK != AccessibleObjectFromWindow( hWnd, OBJID_CARET, IID_IAccessible, (void **) & pAccCaret ) )
    {
        OutputDebugString( TEXT("TryFindCaret: AccessibleObjectFromWindow failed") );
        return FALSE;
    }

    // Now get location of the caret... (will fail if caret is invisible)
    HRESULT hr = pAccCaret->accLocation( & prc->left, & prc->top, & prc->right, & prc->bottom, varCaret );
    pAccCaret->Release();

    if( hr != S_OK )
    {
        // Error, or caret is currently invisible.
        return FALSE;
    }

    // Convert accLocation's left/right/width/height to left/right/top/bottom...
    prc->right += prc->left;
    prc->bottom += prc->top;
	

    // All done!
    return TRUE;
}

BOOL IsFocussedItem( HWND hWnd, IAccessible * pAcc, VARIANT varChild )
{
	
	TCHAR buffer[100];

	GetClassName(hWnd,buffer,100); 
	// Is it toolbar, We cannot determine who had focus!!!
	if ( (lstrcmpi(buffer, TEXT("ToolbarWindow32")) == 0) ||
		(lstrcmpi(buffer, TEXT("Internet Explorer_Server")) == 0))
			return TRUE;
	
	VARIANT varState;
	HRESULT hr;
	
	VariantInit(&varState); 

	hr = pAcc->get_accState(varChild, &varState);

	if ( hr == S_OK )
	{
		if ( ! (varState.lVal & STATE_SYSTEM_FOCUSED) )
		    return FALSE;
	}

	return TRUE;
}