887 lines
30 KiB
C++
887 lines
30 KiB
C++
// --------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
|