Initial revision


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/wm_hooks/wm_hooks.cxx b/wm_hooks/wm_hooks.cxx
new file mode 100644
index 0000000..6923db7
--- /dev/null
+++ b/wm_hooks/wm_hooks.cxx
@@ -0,0 +1,462 @@
+/* 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;
+}