|
|
#include "precomp.h"
//
// IM.CPP
// Input Manager, NT specific code
//
#define MLZ_FILE_ZONE ZONE_INPUT
//
// OSI_InstallControlledHooks()
//
// Installs/removes input hooks for control
//
BOOL WINAPI OSI_InstallControlledHooks(BOOL fEnable, BOOL fDesktop) { BOOL rc = FALSE;
DebugEntry(OSI_InstallControlledHooks);
if (fEnable) { //
// Create the service thread, it will install the hooks.
//
ASSERT(!g_imNTData.imLowLevelInputThread);
if (!DCS_StartThread(IMLowLevelInputProcessor)) { ERROR_OUT(( "Failed to create LL IM thread")); DC_QUIT; } } else { if (g_imNTData.imLowLevelInputThread != 0) { PostThreadMessage( g_imNTData.imLowLevelInputThread, WM_QUIT, 0, 0); g_imNTData.imLowLevelInputThread = 0; } }
if (fDesktop) { rc = TRUE; } else { rc = OSI_InstallHighLevelMouseHook(fEnable); }
DC_EXIT_POINT: DebugExitBOOL(OSI_InstallControlledHooks, rc); return(rc); }
// Name: IMLowLevelInputProcessor
//
// Purpose: Main function for the low-level input handler thread.
//
// Returns: wParam of the WM_QUIT message.
//
// Params: syncObject - sync object that allows this thread to signal
// the creating thread via COM_SignalThreadStarted.
//
// Operation: This function is the start point for the low-level input
// handler thread.
//
// We raise the priority of this thread to:
// (a) ensure that we avoid hitting the low-level callback
// timeout - which would cause us to miss events.
// (b) minimize visible mouse movement lag on the screen.
//
// The thread installs the low-level hooks and enters a
// GetMessage/DispatchMessage loop which handles the low-level
// callbacks.
//
// The Share Core sends the thread a WM_QUIT message to
// terminate it, which causes it to exit the message loop and
// removes the low-level hooks before it terminates.
//
DWORD WINAPI IMLowLevelInputProcessor(LPVOID hEventWait) { MSG msg; UINT rc = 0;
DebugEntry(IMLowLevelInputProcessor);
TRACE_OUT(( "Thread started..."));
//
// Give ourseleves the highest possible priority (within our process
// priority class) to ensure that the low-level events are serviced as
// soon as possible.
//
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
g_imNTData.imLowLevelInputThread = GetCurrentThreadId();
//
// Install low-level input hooks.
//
g_imNTData.imhLowLevelMouseHook = SetWindowsHookEx( WH_MOUSE_LL, IMLowLevelMouseProc, g_asInstance, 0 );
g_imNTData.imhLowLevelKeyboardHook = SetWindowsHookEx( WH_KEYBOARD_LL, IMLowLevelKeyboardProc, g_asInstance, 0 );
//
// We're done with our init code, for better or for worse. Let the
// calling thread continue.
//
SetEvent((HANDLE)hEventWait);
if ( (g_imNTData.imhLowLevelMouseHook == NULL) || (g_imNTData.imhLowLevelKeyboardHook == NULL) ) { ERROR_OUT(( "SetWindowsHookEx failed: hMouse(%u) hKeyboard(%u)", g_imNTData.imhLowLevelMouseHook, g_imNTData.imhLowLevelKeyboardHook )); DC_QUIT; }
//
// Do our message loop to get events
//
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
//
// Remove hooks
//
if (g_imNTData.imhLowLevelMouseHook != NULL) { UnhookWindowsHookEx(g_imNTData.imhLowLevelMouseHook); g_imNTData.imhLowLevelMouseHook = NULL; }
if (g_imNTData.imhLowLevelKeyboardHook != NULL) { UnhookWindowsHookEx(g_imNTData.imhLowLevelKeyboardHook); g_imNTData.imhLowLevelKeyboardHook = NULL; }
DC_EXIT_POINT: DebugExitDWORD(IMLowLevelInputProcessor, rc); return(rc); }
//
// Name: IMOtherDesktopProc()
//
// This allows us to inject (but not block) input into other desktops
// besides default, where the user's desktop resides. Specifically, the
// winlogon desktop and/or the screensaver desktop.
//
// This is trickier than it might seem, because the winlogon desktop is
// always around, but the screen saver one is transitory.
//
// The periodic SWL_ code, called when hosting, checks for the current
// desktop and if it's switched posts us a message so we can change our
// desktop and our hooks.
//
DWORD WINAPI IMOtherDesktopProc(LPVOID hEventWait) { MSG msg; UINT rc = 0; HDESK hDesktop; GUIEFFECTS effects;
DebugEntry(IMOtherDesktopProc);
TRACE_OUT(("Other desktop thread started..."));
g_imNTData.imOtherDesktopThread = GetCurrentThreadId();
//
// Start out attached to the WinLogon desktop because it's always
// around.
//
// Set our desktop to the winlogon desktop
hDesktop = OpenDesktop(NAME_DESKTOP_WINLOGON, 0, FALSE, DESKTOP_JOURNALPLAYBACK);
if ( !hDesktop ) { WARNING_OUT(("OpenDesktop failed: %ld", GetLastError())); DC_QUIT; } else if (!SetThreadDesktop (hDesktop)) { WARNING_OUT(("SetThreadDesktop failed: %ld", GetLastError())); DC_QUIT; }
//
// Attempt to load the driver dynamically on this thread also.
//
if (g_asNT5) { OSI_InitDriver50(TRUE); }
// Let the calling thread continue.
SetEvent((HANDLE)hEventWait);
ZeroMemory(&effects, sizeof(effects));
while (GetMessage(&msg, NULL, 0, 0)) { switch(msg.message) { case OSI_WM_MOUSEINJECT: mouse_event( LOWORD(msg.wParam), // flags
HIWORD(msg.lParam), // x
LOWORD(msg.lParam), // y
HIWORD(msg.wParam), // mouseData
0); // dwExtraInfo
break;
case OSI_WM_KEYBDINJECT: keybd_event( (BYTE)(LOWORD(msg.lParam)), // vkCode
(BYTE)(HIWORD(msg.lParam)), // scanCode
(DWORD)msg.wParam, // flags
0); // dwExtraInfo
break;
case OSI_WM_DESKTOPREPAINT: USR_RepaintWindow(NULL); break;
case OSI_WM_INJECTSAS: { HWND hwndSAS;
if ( hwndSAS = FindWindow("SAS window class",NULL)) { PostMessage(hwndSAS,WM_HOTKEY,0, MAKELONG(0x8000|MOD_ALT|MOD_CONTROL,VK_DELETE)); } else { WARNING_OUT(("SAS window not found, on screensaver desktop")); } break; }
case OSI_WM_DESKTOPSWITCH: { HDESK hDesktopNew;
TRACE_OUT(("OSI_WM_DESKTOPSWITCH: switching desktop from %d to %d", msg.wParam, msg.lParam));
if (msg.lParam == DESKTOP_SCREENSAVER) { // We're switching TO the screensaver, attach to it.
TRACE_OUT(("Switching TO screensaver")); hDesktopNew = OpenDesktop(NAME_DESKTOP_SCREENSAVER, 0, FALSE, DESKTOP_JOURNALPLAYBACK); } else if (msg.wParam == DESKTOP_SCREENSAVER) { //
// We're switching FROM the screensaver, reattach to
// winlogon
//
TRACE_OUT(("Switching FROM screensaver")); hDesktopNew = OpenDesktop(NAME_DESKTOP_WINLOGON, 0, FALSE, DESKTOP_JOURNALPLAYBACK); } else { hDesktopNew = NULL; }
if (hDesktopNew != NULL) { if (!SetThreadDesktop(hDesktopNew)) { WARNING_OUT(("SetThreadDesktop to 0x%08x, type %d failed", hDesktopNew, msg.lParam)); } else { CloseHandle(hDesktop); hDesktop = hDesktopNew; } } break; }
case OSI_WM_SETGUIEFFECTS: { HET_SetGUIEffects((msg.wParam != 0), &effects); break; } } }
DC_EXIT_POINT:
if (g_asNT5) { OSI_InitDriver50(FALSE); }
if (hDesktop) { CloseHandle(hDesktop); }
g_imNTData.imOtherDesktopThread = 0;
DebugExitDWORD(IMOtherDesktopProc, rc); return(rc); }
//
// IMLowLevelMouseProc()
// NT callback for low-level mouse events.
//
// It is installed and called on a secondary thread with high priority to
// service the APC call outs. It follows the windows hook conventions for
// parameters and return values--zero to accept the event, non-zero to
// discard.
//
//
LRESULT CALLBACK IMLowLevelMouseProc ( int nCode, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; PMSLLHOOKSTRUCT pMouseEvent;
DebugEntry(IMLowLevelMouseProc);
pMouseEvent = (PMSLLHOOKSTRUCT)lParam;
//
// If this isn't for an event that is happening or it's one we
// injected ourself, pass it through and no need for processing.
//
if ((nCode != HC_ACTION) || (pMouseEvent->flags & LLMHF_INJECTED)) { DC_QUIT; }
//
// This is a local user event. If controlled, throw it away. Unless
// it's a click, in that case post a REVOKECONTROL message.
//
if (g_imSharedData.imControlled) { //
// If this is a button click, take control back
//
if ((wParam == WM_LBUTTONDOWN) || (wParam == WM_RBUTTONDOWN) || (wParam == WM_MBUTTONDOWN)) { //
// Don't take control back if this is unattended.
//
if (!g_imSharedData.imUnattended) { PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, 0, 0); } }
// Swallow event.
rc = 1; }
DC_EXIT_POINT: //
// Don't pass on to the next hook (if there is one) if we are
// discarding the event.
//
if (!rc) { rc = CallNextHookEx(g_imNTData.imhLowLevelMouseHook, nCode, wParam, lParam); }
DebugExitDWORD(IMLowLevelMouseProc, rc); return(rc); }
// Name: IMLowLevelKeyboardProc
//
// Purpose: Windows callback function for low-level keyboard events.
//
// Returns: 0 if event is to be passed on to USER.
// 1 if event is to be discarded.
//
// Params: Low-level callback params (see Windows documentation).
//
// Operation: Determines whether to allow the given event into USER.
//
// We always pass on injected events.
// The Control Arbitrator determines whether local events are
// passed on.
//
LRESULT CALLBACK IMLowLevelKeyboardProc ( int nCode, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; PKBDLLHOOKSTRUCT pKbdEvent;
DebugEntry(IMLowLevelKeyboardProc);
pKbdEvent = (PKBDLLHOOKSTRUCT)lParam;
//
// If this isn't for an action or it's an event we ourself originated,
// let it through, and do no processing.
//
if ((nCode != HC_ACTION) || (pKbdEvent->flags & LLKHF_INJECTED)) { DC_QUIT; }
if (g_imSharedData.imControlled) { if (!(pKbdEvent->flags & LLKHF_UP)) { //
// This is a key down. Take control back, and kill control
// allowability if it's the ESC key.
//
if ((pKbdEvent->vkCode & 0x00FF) == VK_ESCAPE || g_imSharedData.imUnattended) { // ESC key always disallows control, even in unattended mode
PostMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, FALSE, 0); } else 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.
//
// LAURABU: How do we fix this in new model? Post a toggle-key
// message and undo it (fake press)?
//
if (!IM_KEY_IS_TOGGLE(pKbdEvent->vkCode & 0x00FF)) rc = 1; }
DC_EXIT_POINT: //
// Don't pass on to the next hook if we are swallowing the event.
//
if (!rc) { rc = CallNextHookEx(g_imNTData.imhLowLevelKeyboardHook, nCode, wParam, lParam); }
DebugExitDWORD(IMLowLevelKeyboardProc, rc); return(rc); }
//
// IMInjectMouseEvent()
// NT-specific version to inject mouse events into the local system
//
void WINAPI OSI_InjectMouseEvent ( DWORD flags, LONG x, LONG y, DWORD mouseData, DWORD dwExtraInfo ) { TRACE_OUT(("Before MOUSE inject: %08lx, %08lx %08lx", flags, mouseData, dwExtraInfo));
mouse_event(flags, (DWORD)x, (DWORD)y, mouseData, dwExtraInfo);
if ( g_imNTData.imOtherDesktopThread ) { // Stuff these dword parameters through WORDS
// need to make sure we don't clip anything
ASSERT(!(flags & 0xffff0000)); //ASSERT(!(mouseData & 0xffff0000)); BUGBUG possible loss
ASSERT(!(x & 0xffff0000)); ASSERT(!(y & 0xffff0000));
PostThreadMessage( g_imNTData.imOtherDesktopThread, OSI_WM_MOUSEINJECT, MAKEWPARAM((WORD)flags,(WORD)mouseData), MAKELPARAM((WORD)y, (WORD)x )); }
TRACE_OUT(("After MOUSE inject")); }
//
// OSI_InjectSAS()
// NT-specific version to inject ctrl+alt+del into the local system
//
void WINAPI OSI_InjectCtrlAltDel(void) { if ( g_imNTData.imOtherDesktopThread ) { PostThreadMessage( g_imNTData.imOtherDesktopThread, OSI_WM_INJECTSAS, 0, 0 ); } else { WARNING_OUT(("Ignoring SAS Injection attempt")); } }
//
// OSI_InjectKeyboardEvent()
// NT-specific version to inject keyboard events into the local system
//
void WINAPI OSI_InjectKeyboardEvent ( DWORD flags, WORD vkCode, WORD scanCode, DWORD dwExtraInfo ) { TRACE_OUT(("Before KEY inject: %04lx, {%04x, %04x}, %04lx", flags, vkCode, scanCode, dwExtraInfo));
keybd_event((BYTE)vkCode, (BYTE)scanCode, flags, dwExtraInfo);
if ( g_imNTData.imOtherDesktopThread ) { PostThreadMessage( g_imNTData.imOtherDesktopThread, OSI_WM_KEYBDINJECT, (WPARAM)flags, MAKELPARAM(vkCode, scanCode)); }
TRACE_OUT(("After KEY inject")); }
//
// OSI_DesktopSwitch()
// NT-specific, called when we think the current desktop has changed.
//
void WINAPI OSI_DesktopSwitch ( UINT desktopFrom, UINT desktopTo ) { DebugEntry(OSI_DesktopSwitch);
if (g_imNTData.imOtherDesktopThread) { PostThreadMessage( g_imNTData.imOtherDesktopThread, OSI_WM_DESKTOPSWITCH, desktopFrom, desktopTo); }
DebugExitVOID(OSI_DesktopSwitch); }
|