tigervnc/wm_hooks/wm_hooks.cxx
Constantin Kaplinsky 47ed8d321c Initial revision
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2 3789f03b-4d11-0410-bbf8-ca57d06f2519
2004-10-08 09:43:57 +00:00

463 lines
12 KiB
C++

/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
// -=- wm_hooks.cxx
//
// Window Message Hooks Dynamic Link library
#define _WIN32_WINNT 0x0400
#include <tchar.h>
#include <wm_hooks/wm_hooks.h>
UINT WM_HK_PingThread = RegisterWindowMessage(_T("RFB.WM_Hooks.PingThread"));
UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));
UINT WM_Hooks_WindowChanged() {
return WM_HK_WindowChanged;
}
UINT WM_HK_WindowClientAreaChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowClientAreaChanged"));
UINT WM_Hooks_WindowClientAreaChanged() {
return WM_HK_WindowClientAreaChanged;
}
UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));
UINT WM_Hooks_WindowBorderChanged() {
return WM_HK_WindowBorderChanged;
}
UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));
UINT WM_Hooks_RectangleChanged() {
return WM_HK_RectangleChanged;
}
UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));
UINT WM_Hooks_CursorChanged() {
return WM_HK_CursorChanged;
}
#ifdef _DEBUG
UINT WM_HK_Diagnostic = RegisterWindowMessage(_T("RFB.WM_Hooks.Diagnostic"));
UINT WM_Hooks_Diagnostic() {
return WM_HK_Diagnostic;
}
#endif
ATOM ATOM_Popup_Selection = GlobalAddAtom(_T("RFB.WM_Hooks.PopupSelectionAtom"));
//
// -=- DLL entry point
//
HINSTANCE dll_instance = 0;
BOOL WINAPI DllMain(HANDLE instance, ULONG reason, LPVOID reserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
dll_instance = (HINSTANCE)instance;
return TRUE;
case DLL_PROCESS_DETACH:
return TRUE;
case DLL_THREAD_DETACH:
WM_Hooks_Remove(GetCurrentThreadId());
return TRUE;
default:
return TRUE;
};
}
//
// -=- Display update hooks
//
#pragma data_seg(".WM_Hooks_Shared")
DWORD hook_owner = 0;
DWORD hook_target = 0;
HHOOK hook_CallWndProc = 0;
HHOOK hook_CallWndProcRet = 0;
HHOOK hook_GetMessage = 0;
HHOOK hook_DialogMessage = 0;
BOOL enable_cursor_shape = FALSE;
HCURSOR cursor = 0;
#ifdef _DEBUG
UINT diagnostic_min=1;
UINT diagnostic_max=0;
#endif
#pragma data_seg()
#ifdef _DEBUG
DLLEXPORT void WM_Hooks_SetDiagnosticRange(UINT min, UINT max) {
diagnostic_min = min; diagnostic_max=max;
}
#endif
bool NotifyHookOwner(UINT event, WPARAM wParam, LPARAM lParam) {
if (hook_owner) {
return PostThreadMessage(hook_owner, event, wParam, lParam)!=0;
/*
if (last_event)
return PostThreadMessage(hook_owner, last_event, last_wParam, last_lParam);
last_event = event;
last_wParam = wParam;
last_lParam = lParam;
return true;
*/
}
return false;
}
bool NotifyWindow(HWND hwnd, UINT msg) {
return NotifyHookOwner(WM_HK_WindowChanged, msg, (LPARAM)hwnd);
}
bool NotifyWindowBorder(HWND hwnd, UINT msg) {
return NotifyHookOwner(WM_HK_WindowBorderChanged, msg, (LPARAM)hwnd);
}
bool NotifyWindowClientArea(HWND hwnd, UINT msg) {
return NotifyHookOwner(WM_HK_WindowClientAreaChanged, msg, (LPARAM)hwnd);
}
bool NotifyRectangle(RECT* rect) {
WPARAM w = MAKELONG((SHORT)rect->left, (SHORT)rect->top);
LPARAM l = MAKELONG((SHORT)rect->right, (SHORT)rect->bottom);
return NotifyHookOwner(WM_HK_RectangleChanged, w, l);
}
bool NotifyCursor(HCURSOR cursor) {
return NotifyHookOwner(WM_HK_CursorChanged, 0, (LPARAM)cursor);
}
void ProcessWindowMessage(UINT msg, HWND wnd, WPARAM wParam, LPARAM lParam) {
#ifdef _DEBUG
if ((msg >= diagnostic_min) && (msg <= diagnostic_max))
PostThreadMessage(hook_owner, WM_HK_Diagnostic, msg, (LPARAM)wnd);
#endif
if (!IsWindowVisible(wnd)) return;
switch (msg) {
// -=- Border update events
case WM_NCPAINT:
case WM_NCACTIVATE:
NotifyWindowBorder(wnd, msg);
break;
// -=- Client area update events
case BM_SETCHECK:
case BM_SETSTATE:
case EM_SETSEL:
case WM_CHAR:
case WM_ENABLE:
case WM_KEYUP:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_PALETTECHANGED:
case WM_RBUTTONUP:
case WM_SYSCOLORCHANGE:
case WM_SETTEXT:
case WM_SETFOCUS:
//case WM_TIMER:
NotifyWindowClientArea(wnd, msg);
break;
case WM_HSCROLL:
case WM_VSCROLL:
if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
NotifyWindow(wnd, msg);
break;
case WM_WINDOWPOSCHANGING:
case WM_DESTROY:
{
RECT wrect;
if (GetWindowRect(wnd, &wrect)) {
NotifyRectangle(&wrect);
}
}
break;
case WM_PAINT:
// *** could improve this
NotifyWindowClientArea(wnd, msg);
break;
// Handle pop-up menus appearing
case 482:
NotifyWindow(wnd, 482);
break;
// Handle pop-up menus having items selected
case 485:
{
HANDLE prop = GetProp(wnd, (LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0));
if (prop != (HANDLE) wParam) {
NotifyWindow(wnd, 485);
SetProp(wnd,
(LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0),
(HANDLE) wParam);
}
}
break;
case WM_NCMOUSEMOVE:
case WM_MOUSEMOVE:
if (enable_cursor_shape) {
HCURSOR new_cursor = GetCursor();
if (new_cursor != cursor) {
cursor = new_cursor;
NotifyCursor(cursor);
}
}
break;
/* ***
if (prf_use_GetUpdateRect)
{
HRGN region;
region = CreateRectRgn(0, 0, 0, 0);
// Get the affected region
if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
{
int buffsize;
UINT x;
RGNDATA *buff;
POINT TopLeft;
// Get the top-left point of the client area
TopLeft.x = 0;
TopLeft.y = 0;
if (!ClientToScreen(hWnd, &TopLeft))
break;
// Get the size of buffer required
buffsize = GetRegionData(region, 0, 0);
if (buffsize != 0)
{
buff = (RGNDATA *) new BYTE [buffsize];
if (buff == NULL)
break;
// Now get the region data
if(GetRegionData(region, buffsize, buff))
{
for (x=0; x<(buff->rdh.nCount); x++)
{
// Obtain the rectangles from the list
RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
SendDeferredUpdateRect(
hWnd,
(SHORT) (TopLeft.x + urect->left),
(SHORT) (TopLeft.y + urect->top),
(SHORT) (TopLeft.x + urect->right),
(SHORT) (TopLeft.y + urect->bottom)
);
}
}
delete [] buff;
}
}
// Now free the region
if (region != NULL)
DeleteObject(region);
}
*/
};
}
LRESULT CALLBACK HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
CWPSTRUCT* info = (CWPSTRUCT*) lParam;
ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
}
return CallNextHookEx(hook_CallWndProc, nCode, wParam, lParam);
}
LRESULT CALLBACK HookCallWndProcRet(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
CWPRETSTRUCT* info = (CWPRETSTRUCT*) lParam;
ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
}
return CallNextHookEx(hook_CallWndProcRet, nCode, wParam, lParam);
}
LRESULT CALLBACK HookGetMessage(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
if (wParam & PM_REMOVE) {
MSG* msg = (MSG*) lParam;
ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
}
}
return CallNextHookEx(hook_GetMessage, nCode, wParam, lParam);
}
LRESULT CALLBACK HookDialogMessage(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
MSG* msg = (MSG*) lParam;
ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
}
return CallNextHookEx(hook_DialogMessage, nCode, wParam, lParam);
}
// - WM_Hooks_Install
BOOL WM_Hooks_Install(DWORD owner, DWORD thread) {
// - Are there already hooks set?
if (hook_owner) {
if (!PostThreadMessage(hook_owner, WM_HK_PingThread, 0, 0)) {
WM_Hooks_Remove(hook_owner);
} else {
return FALSE;
}
}
// - Initialise the hooks
hook_owner = owner;
hook_target = thread;
hook_CallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, dll_instance, thread);
//hook_CallWndProcRet = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProcRet, dll_instance, thread);
hook_GetMessage = SetWindowsHookEx(WH_GETMESSAGE, HookGetMessage, dll_instance, thread);
hook_DialogMessage = SetWindowsHookEx(WH_SYSMSGFILTER, HookDialogMessage, dll_instance, thread);
if (!hook_CallWndProc /*|| !hook_CallWndProcRet*/ || !hook_GetMessage || !hook_DialogMessage) {
WM_Hooks_Remove(owner);
return FALSE;
}
return TRUE;
}
// - WM_Hooks_Remove
BOOL WM_Hooks_Remove(DWORD owner) {
if (owner != hook_owner) return FALSE;
if (hook_CallWndProc) {
UnhookWindowsHookEx(hook_CallWndProc);
hook_CallWndProc = 0;
}
if (hook_CallWndProcRet) {
UnhookWindowsHookEx(hook_CallWndProcRet);
hook_CallWndProcRet = 0;
}
if (hook_GetMessage) {
UnhookWindowsHookEx(hook_GetMessage);
hook_GetMessage = 0;
}
if (hook_DialogMessage) {
UnhookWindowsHookEx(hook_DialogMessage);
hook_DialogMessage = 0;
}
hook_owner = 0;
hook_target = 0;
return TRUE;
}
//
// -=- User input hooks
//
#pragma data_seg(".WM_Hooks_Shared")
HHOOK hook_keyboard = 0;
HHOOK hook_pointer = 0;
bool enable_real_ptr = true;
bool enable_synth_ptr = true;
bool enable_real_kbd = true;
bool enable_synth_kbd = true;
#pragma data_seg()
#ifdef WH_KEYBOARD_LL
LRESULT CALLBACK HookKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
KBDLLHOOKSTRUCT* info = (KBDLLHOOKSTRUCT*) lParam;
bool real_event = (info->flags & LLKHF_INJECTED) == 0;
if ((real_event && !enable_real_kbd) ||
(!real_event && !enable_synth_kbd)) {
return 1;
}
}
return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
}
LRESULT CALLBACK HookPointerHook(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
MSLLHOOKSTRUCT* info = (MSLLHOOKSTRUCT*) lParam;
bool real_event = (info->flags & LLMHF_INJECTED) == 0;
if ((real_event && !enable_real_ptr) ||
(!real_event && !enable_synth_ptr)) {
return 1;
}
}
return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
}
bool RefreshInputHooks() {
bool success = true;
bool set_ptr_hook = !enable_real_ptr || !enable_synth_ptr;
bool set_kbd_hook = !enable_real_kbd || !enable_synth_kbd;
if (hook_keyboard && !set_kbd_hook) {
UnhookWindowsHookEx(hook_keyboard);
hook_keyboard = 0;
}
if (hook_pointer && !set_ptr_hook) {
UnhookWindowsHookEx(hook_pointer);
hook_pointer = 0;
}
if (!hook_keyboard && set_kbd_hook) {
hook_keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboardHook, dll_instance, 0);
if (!hook_keyboard) success = false;
}
if (!hook_pointer && set_ptr_hook) {
hook_pointer = SetWindowsHookEx(WH_MOUSE_LL, HookPointerHook, dll_instance, 0);
if (!hook_pointer) success = false;
}
return success;
}
#else
#pragma message("WARNING: low-level mouse and keyboard hooks not supported")
#endif
// - WM_Hooks_EnableRealInputs
BOOL WM_Hooks_EnableRealInputs(BOOL pointer, BOOL keyboard) {
#ifdef WH_KEYBOARD_LL
enable_real_ptr = pointer!=0;
enable_real_kbd = keyboard!=0;
return RefreshInputHooks();
#else
return FALSE;
#endif
}
// - WM_Hooks_EnableSynthInputs
BOOL WM_Hooks_EnableSynthInputs(BOOL pointer, BOOL keyboard) {
#ifdef WH_KEYBOARD_LL
enable_synth_ptr = pointer!=0;
enable_synth_kbd = keyboard!=0;
return RefreshInputHooks();
#else
return FALSE;
#endif
}
// - WM_Hooks_EnableCursorShape
BOOL WM_Hooks_EnableCursorShape(BOOL enable) {
enable_cursor_shape = enable;
return FALSE;
}