windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/as16/im.c

647 lines
15 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
//
// IM.C
// Input Manager
//
// Copyright(c) 1997-
//
#include <as16.h>
//
// IM_DDInit()
// This creates the resources we need for controlling
//
BOOL IM_DDInit(void)
{
UINT uSel;
BOOL rc = FALSE;
DebugEntry(IM_DDInit);
//
// Create interrupt patches for mouse_event and keybd_event
//
uSel = CreateFnPatch(mouse_event, ASMMouseEvent, &g_imPatches[IM_MOUSEEVENT], 0);
if (!uSel)
{
ERROR_OUT(("Couldn't find mouse_event"));
DC_QUIT;
}
g_imPatches[IM_MOUSEEVENT].fInterruptable = TRUE;
if (!CreateFnPatch(keybd_event, ASMKeyboardEvent, &g_imPatches[IM_KEYBOARDEVENT], uSel))
{
ERROR_OUT(("Couldn't find keybd_event"));
DC_QUIT;
}
g_imPatches[IM_KEYBOARDEVENT].fInterruptable = TRUE;
//
// Create patch for SignalProc32 so we can find out when fault/hung
// dialogs from KERNEL32 come up.
//
if (!CreateFnPatch(SignalProc32, DrvSignalProc32, &g_imPatches[IM_SIGNALPROC32], 0))
{
ERROR_OUT(("Couldn't patch SignalProc32"));
DC_QUIT;
}
//
// Create patches for win16lock pulsing in 16-bit app modal loops
//
uSel = CreateFnPatch(RealGetCursorPos, DrvGetCursorPos, &g_imPatches[IM_GETCURSORPOS], 0);
if (!uSel)
{
ERROR_OUT(("Couldn't find GetCursorPos"));
DC_QUIT;
}
if (!CreateFnPatch(GetAsyncKeyState, DrvGetAsyncKeyState, &g_imPatches[IM_GETASYNCKEYSTATE], 0))
{
ERROR_OUT(("Couldn't find GetAsyncKeyState"));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(IM_DDInit, rc);
return(rc);
}
//
// IM_DDTerm()
// This cleans up any resources we needed for controlling
//
void IM_DDTerm(void)
{
IM_PATCH imPatch;
DebugEntry(IM_DDTerm);
//
// Force undo of hooks
//
OSIInstallControlledHooks16(FALSE, FALSE);
//
// Destroy patches
//
for (imPatch = IM_FIRST; imPatch < IM_MAX; imPatch++)
{
DestroyFnPatch(&g_imPatches[imPatch]);
}
DebugExitVOID(IM_DDTerm);
}
//
// OSIInstallControlledHooks16()
//
// This installs/removes the input hooks we need to allow this machine to
// be controlled.
//
BOOL WINAPI OSIInstallControlledHooks16(BOOL fEnable, BOOL fDesktop)
{
BOOL rc = TRUE;
IM_PATCH imPatch;
DebugEntry(OSIInstallControlledHooks16);
if (fEnable)
{
if (!g_imWin95Data.imLowLevelHooks)
{
g_imWin95Data.imLowLevelHooks = TRUE;
g_imMouseDowns = 0;
//
// GlobalSmartPageLock() stuff we need:
// * Our code segment
// * Our data segment
//
GlobalSmartPageLock((HGLOBAL)SELECTOROF((LPVOID)DrvMouseEvent));
GlobalSmartPageLock((HGLOBAL)SELECTOROF((LPVOID)&g_imSharedData));
//
// Install hooks
//
for (imPatch = IM_FIRST; imPatch < IM_MAX; imPatch++)
{
EnableFnPatch(&g_imPatches[imPatch], PATCH_ACTIVATE);
}
}
//
// Install high-level mouse hook
if (!fDesktop)
{
if (!g_imWin95Data.imhHighLevelMouseHook)
{
//
// Install the mouse hook.
//
g_imWin95Data.imhHighLevelMouseHook = SetWindowsHookEx(WH_MOUSE,
IMMouseHookProc, g_hInstAs16, 0);
if (!g_imWin95Data.imhHighLevelMouseHook)
{
ERROR_OUT(("Failed to install mouse hook"));
rc = FALSE;
}
}
}
}
else
{
if (g_imWin95Data.imLowLevelHooks)
{
//
// Uninstall hooks
//
for (imPatch = IM_MAX; imPatch > 0; imPatch--)
{
EnableFnPatch(&g_imPatches[imPatch-1], PATCH_DEACTIVATE);
}
//
// GlobalSmartUnPageLock() stuff we needed
//
GlobalSmartPageUnlock((HGLOBAL)SELECTOROF((LPVOID)&g_imSharedData));
GlobalSmartPageUnlock((HGLOBAL)SELECTOROF((LPVOID)DrvMouseEvent));
g_imWin95Data.imLowLevelHooks = FALSE;
}
if (!fDesktop)
{
if (g_imWin95Data.imhHighLevelMouseHook)
{
//
// Remove the mouse hook.
//
UnhookWindowsHookEx(g_imWin95Data.imhHighLevelMouseHook);
g_imWin95Data.imhHighLevelMouseHook = NULL;
}
}
}
DebugExitBOOL(OSIInstallControlledHooks16, rc);
return(rc);
}
#pragma optimize("gle", off)
void IMInject(BOOL fOn)
{
if (fOn)
{
#ifdef DEBUG
DWORD tmp;
//
// Disable interrupts then turn injection global on
// But before we do this, we must make sure that we aren't going
// to have to fault in a new stack page. Since this is on a 32-bit
// thread, we will be in trouble.
//
tmp = GetSelectorBase(SELECTOROF(((LPVOID)&fOn))) +
OFFSETOF((LPVOID)&fOn);
if ((tmp & 0xFFFFF000) != ((tmp - 0x100) & 0xFFFFF000))
{
ERROR_OUT(("Close to page boundary on 32-bit stack %08lx", tmp));
}
#endif // DEBUG
_asm cli
g_imWin95Data.imInjecting = TRUE;
}
else
{
//
// Turn injection global off then enable interrupts
//
g_imWin95Data.imInjecting = FALSE;
_asm sti
}
}
#pragma optimize("", on)
//
// OSIInjectMouseEvent16()
//
void WINAPI OSIInjectMouseEvent16
(
UINT flags,
int x,
int y,
UINT mouseData,
DWORD dwExtraInfo
)
{
DebugEntry(OSIInjectMouseEvent16);
if (flags & IM_MOUSEEVENTF_BUTTONDOWN_FLAGS)
{
++g_imMouseDowns;
}
//
// We disable interrupts, call the real mouse_event, reenable
// interrupts. That way our mouse_event patch is serialized.
// And we can check imInjecting.
//
IMInject(TRUE);
CallMouseEvent(flags, x, y, mouseData, LOWORD(dwExtraInfo), HIWORD(dwExtraInfo));
IMInject(FALSE);
if (flags & IM_MOUSEEVENTF_BUTTONUP_FLAGS)
{
--g_imMouseDowns;
ASSERT(g_imMouseDowns >= 0);
}
DebugExitVOID(OSIInjectMouseEvent16);
}
//
// OSIInjectKeyboardEvent16()
//
void WINAPI OSIInjectKeyboardEvent16
(
UINT flags,
WORD vkCode,
WORD scanCode,
DWORD dwExtraInfo
)
{
DebugEntry(OSIInjectKeyboardEvent16);
//
// First, fix up the flags
//
if (flags & KEYEVENTF_KEYUP)
{
// Put 0x80 in the HIBYTE of vkCode, this means a keyup
vkCode = (WORD)(BYTE)vkCode | USERKEYEVENTF_KEYUP;
}
if (flags & KEYEVENTF_EXTENDEDKEY)
{
// Put 0x01 in the HIBYTE of scanCode, this means extended
scanCode = (WORD)(BYTE)scanCode | USERKEYEVENTF_EXTENDEDKEY;
}
//
// We disable interrupts, call the real keybd_event, reenable
// interrupts. That way our keybd_event patch is serialized.
// And we can check the imfInject variable.
//
IMInject(TRUE);
CallKeyboardEvent(vkCode, scanCode, LOWORD(dwExtraInfo), HIWORD(dwExtraInfo));
IMInject(FALSE);
DebugExitVOID(OSIInjectKeyboardEvent16);
}
//
// Win16lock pulse points when injecting mouse down/up sequences into 16-bit
// modal loop apps.
//
//
// IMCheckWin16LockPulse()
// This pulses the win16lock if we are in the middle of injecting a mouse
// down-up sequence to a 16-bit app shared on this machine. We do this to
// prevent deadlock, caused by 16-bit dudes going into modal loops, not
// releasing the win16lock. Our 32-bit thread would get stuck on the win16
// lock trying to play back the rest of the sequence.
//
void IMCheckWin16LockPulse(void)
{
DebugEntry(IMCheckWin16LockPulse);
if ((g_imMouseDowns > 0) &&
(GetProcessDword(0, GPD_FLAGS) & GPF_WIN16_PROCESS))
{
TRACE_OUT(("Pulsing win16lock for 16-bit app; mouse down count %d", g_imMouseDowns));
_LeaveWin16Lock();
_EnterWin16Lock();
TRACE_OUT(("Pulsed win16lock for 16-bit app; mouse down count %d", g_imMouseDowns));
}
DebugExitVOID(IMCheckWin16LockPulse);
}
int WINAPI DrvGetAsyncKeyState(int vk)
{
int retVal;
DebugEntry(DrvGetAsyncKeyState);
// Pulse BEFORE we call to USER
IMCheckWin16LockPulse();
EnableFnPatch(&g_imPatches[IM_GETASYNCKEYSTATE], PATCH_DISABLE);
retVal = GetAsyncKeyState(vk);
EnableFnPatch(&g_imPatches[IM_GETASYNCKEYSTATE], PATCH_ENABLE);
DebugExitBOOL(DrvGetAsyncKeyState, retVal);
return(retVal);
}
//
// DrvGetCursorPos()
//
BOOL WINAPI DrvGetCursorPos(LPPOINT lppt)
{
BOOL retVal;
DebugEntry(DrvGetCursorPos);
// Pulse BEFORE calling USER
IMCheckWin16LockPulse();
EnableFnPatch(&g_imPatches[IM_GETCURSORPOS], PATCH_DISABLE);
retVal = RealGetCursorPos(lppt);
EnableFnPatch(&g_imPatches[IM_GETCURSORPOS], PATCH_ENABLE);
DebugExitBOOL(DrvGetCursorPos, retVal);
return(retVal);
}
//
// IMMouseHookProc()
// High level mouse hook to make sure mice messages are going where we
// think they should when your machine is being controlled.
//
LRESULT CALLBACK IMMouseHookProc
(
int code,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT rc;
BOOL fBlock = FALSE;
LPMOUSEHOOKSTRUCT lpMseHook = (LPMOUSEHOOKSTRUCT)lParam;
DebugEntry(IMMouseHookProc);
if (code < 0)
{
//
// Only pass along
//
DC_QUIT;
}
//
// Decide if we should block this event. We will if it is not destined
// for a hosted window and not destined for a screen saver.
//
if (!HET_WindowIsHosted(lpMseHook->hwnd) &&
!OSIIsWindowScreenSaver16(lpMseHook->hwnd))
{
fBlock = TRUE;
}
TRACE_OUT(("MOUSEHOOK hwnd %04x -> block: %s",
lpMseHook->hwnd,
(fBlock ? (LPSTR)"YES" : (LPSTR)"NO")));
DC_EXIT_POINT:
//
// Call the next hook
//
rc = CallNextHookEx(g_imWin95Data.imhHighLevelMouseHook, code, wParam, lParam);
if (fBlock)
{
//
// To block further processing in USER, return TRUE
//
rc = TRUE;
}
DebugExitDWORD(IMMouseHookProc, rc);
return(rc);
}
//
// DrvMouseEvent()
// mouse_event interrupt patch
//
void WINAPI DrvMouseEvent
(
UINT regAX,
UINT regBX,
UINT regCX,
UINT regDX,
UINT regSI,
UINT regDI
)
{
BOOL fAllow;
//
// If this is injected by us, just pass it through.
//
fAllow = TRUE;
if (g_imWin95Data.imInjecting)
{
DC_QUIT;
}
//
// NOTE:
// flags is in AX
// x coord is in BX
// y coord is in CX
// mousedata is in DX
// dwExtraInfo is in DI, SI
//
if (g_imSharedData.imControlled && !g_imSharedData.imPaused)
{
//
// If this is a button click, take control back
//
if (regAX &
(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_MIDDLEDOWN))
{
if (!g_imSharedData.imUnattended)
{
PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, FALSE, 0);
}
}
if (!g_imSharedData.imSuspended)
fAllow = FALSE;
}
DC_EXIT_POINT:
if (fAllow)
{
EnableFnPatch(&g_imPatches[IM_MOUSEEVENT], PATCH_DISABLE);
CallMouseEvent(regAX, regBX, regCX, regDX, regSI, regDI);
EnableFnPatch(&g_imPatches[IM_MOUSEEVENT], PATCH_ENABLE);
}
}
//
// DrvKeyboardEvent()
// keybd_event interrupt patch
//
void WINAPI DrvKeyboardEvent
(
UINT regAX,
UINT regBX,
UINT regSI,
UINT regDI
)
{
BOOL fAllow;
//
// If this is injected by us, pass it through. Do the same for
// critical errors, since everything is frozen and we can't play back
// input if we wanted to.
//
// If the scan-code (in regBX) is 0 we assume that the input
// is injected by an application (such as an IME) and we don't
// want to block this or take control.
//
fAllow = TRUE;
if (g_imWin95Data.imInjecting || !regBX)
{
DC_QUIT;
}
//
// NOTE:
// vkCode is in AX, LOBYTE is vkCode, HIBYTE is state
// scanCode is in BX
// dwExtraInfo is in DI, SI
//
if (g_imSharedData.imControlled && !g_imSharedData.imPaused)
{
if (!(regAX & USERKEYEVENTF_KEYUP))
{
//
// This is a key down. Take control back (except for ALT key),
// and kill control allowability if it's the ESC key.
//
if (LOBYTE(regAX) == VK_ESCAPE)
{
PostMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, FALSE, 0);
}
else if (LOBYTE(regAX != VK_MENU))
{
if (!g_imSharedData.imUnattended)
{
PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, 0, 0);
}
}
}
//
// Don't discard toggle keys. The enabled/disabled function
// is already set before we see the keystroke. If we discard,
// the lights are incorrect.
//
if (!IM_KEY_IS_TOGGLE(LOBYTE(regAX)) && !g_imSharedData.imSuspended)
{
fAllow = FALSE;
}
}
DC_EXIT_POINT:
if (fAllow)
{
EnableFnPatch(&g_imPatches[IM_KEYBOARDEVENT], PATCH_DISABLE);
CallKeyboardEvent(regAX, regBX, regSI, regDI);
EnableFnPatch(&g_imPatches[IM_KEYBOARDEVENT], PATCH_ENABLE);
}
}
//
// DrvSignalProc32()
// This patches USER's SignalProc32 export and watches for the FORCE_LOCK
// signals. KERNEL32 calls them before/after putting up critical error and
// fault dialogs. That's how we know when one is coming up, and can
// temporarily suspend remote control of your machine so you can dismiss
// them. Usually the thread they are on is boosted so high priority that
// nothing else can run, and so NM can't pump in input from the remote.
//
BOOL WINAPI DrvSignalProc32
(
DWORD dwSignal,
DWORD dwID,
DWORD dwFlags,
WORD hTask16
)
{
BOOL fRet;
DebugEntry(DrvSignalProc32);
if (dwSignal == SIG_PRE_FORCE_LOCK)
{
TRACE_OUT(("Disabling remote control before critical dialog, count %ld",
g_imSharedData.imSuspended));
++g_imSharedData.imSuspended;
}
EnableFnPatch(&g_imPatches[IM_SIGNALPROC32], PATCH_DISABLE);
fRet = SignalProc32(dwSignal, dwID, dwFlags, hTask16);
EnableFnPatch(&g_imPatches[IM_SIGNALPROC32], PATCH_ENABLE);
if (dwSignal == SIG_POST_FORCE_LOCK)
{
--g_imSharedData.imSuspended;
TRACE_OUT(("Enabling remote control after critical dialog, count %ld",
g_imSharedData.imSuspended));
}
DebugExitBOOL(DrvSignalProc32, fRet);
return(fRet);
}