blob: fd01159c6ee45eb28aa6ca36de3543695a79d57e [file] [log] [blame]
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00003 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19// -=- wm_hooks.cxx
20//
21// Window Message Hooks Dynamic Link library
22
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000023#include <tchar.h>
24
25#include <wm_hooks/wm_hooks.h>
26
27UINT WM_HK_PingThread = RegisterWindowMessage(_T("RFB.WM_Hooks.PingThread"));
28
29UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));
30UINT WM_Hooks_WindowChanged() {
31 return WM_HK_WindowChanged;
32}
33
34UINT WM_HK_WindowClientAreaChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowClientAreaChanged"));
35UINT WM_Hooks_WindowClientAreaChanged() {
36 return WM_HK_WindowClientAreaChanged;
37}
38
39UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));
40UINT WM_Hooks_WindowBorderChanged() {
41 return WM_HK_WindowBorderChanged;
42}
43
44UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));
45UINT WM_Hooks_RectangleChanged() {
46 return WM_HK_RectangleChanged;
47}
48
49UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));
50UINT WM_Hooks_CursorChanged() {
51 return WM_HK_CursorChanged;
52}
53
54#ifdef _DEBUG
55UINT WM_HK_Diagnostic = RegisterWindowMessage(_T("RFB.WM_Hooks.Diagnostic"));
56UINT WM_Hooks_Diagnostic() {
57 return WM_HK_Diagnostic;
58}
59#endif
60
61ATOM ATOM_Popup_Selection = GlobalAddAtom(_T("RFB.WM_Hooks.PopupSelectionAtom"));
62
63//
64// -=- DLL entry point
65//
66
67HINSTANCE dll_instance = 0;
68
69BOOL WINAPI DllMain(HANDLE instance, ULONG reason, LPVOID reserved) {
70 switch (reason) {
71 case DLL_PROCESS_ATTACH:
72 dll_instance = (HINSTANCE)instance;
73 return TRUE;
74 case DLL_PROCESS_DETACH:
75 return TRUE;
76 case DLL_THREAD_DETACH:
77 WM_Hooks_Remove(GetCurrentThreadId());
78 return TRUE;
79 default:
80 return TRUE;
81 };
82}
83
84//
85// -=- Display update hooks
86//
87
88#pragma data_seg(".WM_Hooks_Shared")
89DWORD hook_owner = 0;
90DWORD hook_target = 0;
91HHOOK hook_CallWndProc = 0;
92HHOOK hook_CallWndProcRet = 0;
93HHOOK hook_GetMessage = 0;
94HHOOK hook_DialogMessage = 0;
95BOOL enable_cursor_shape = FALSE;
96HCURSOR cursor = 0;
97#ifdef _DEBUG
98UINT diagnostic_min=1;
99UINT diagnostic_max=0;
100#endif
101#pragma data_seg()
102
103#ifdef _DEBUG
104DLLEXPORT void WM_Hooks_SetDiagnosticRange(UINT min, UINT max) {
105 diagnostic_min = min; diagnostic_max=max;
106}
107#endif
108
109bool NotifyHookOwner(UINT event, WPARAM wParam, LPARAM lParam) {
110 if (hook_owner) {
111 return PostThreadMessage(hook_owner, event, wParam, lParam)!=0;
112 /*
113 if (last_event)
114 return PostThreadMessage(hook_owner, last_event, last_wParam, last_lParam);
115 last_event = event;
116 last_wParam = wParam;
117 last_lParam = lParam;
118 return true;
119 */
120 }
121 return false;
122}
123
124bool NotifyWindow(HWND hwnd, UINT msg) {
125 return NotifyHookOwner(WM_HK_WindowChanged, msg, (LPARAM)hwnd);
126}
127bool NotifyWindowBorder(HWND hwnd, UINT msg) {
128 return NotifyHookOwner(WM_HK_WindowBorderChanged, msg, (LPARAM)hwnd);
129}
130bool NotifyWindowClientArea(HWND hwnd, UINT msg) {
131 return NotifyHookOwner(WM_HK_WindowClientAreaChanged, msg, (LPARAM)hwnd);
132}
133bool NotifyRectangle(RECT* rect) {
134 WPARAM w = MAKELONG((SHORT)rect->left, (SHORT)rect->top);
135 LPARAM l = MAKELONG((SHORT)rect->right, (SHORT)rect->bottom);
136 return NotifyHookOwner(WM_HK_RectangleChanged, w, l);
137}
138bool NotifyCursor(HCURSOR cursor) {
139 return NotifyHookOwner(WM_HK_CursorChanged, 0, (LPARAM)cursor);
140}
141
142void ProcessWindowMessage(UINT msg, HWND wnd, WPARAM wParam, LPARAM lParam) {
143#ifdef _DEBUG
144 if ((msg >= diagnostic_min) && (msg <= diagnostic_max))
145 PostThreadMessage(hook_owner, WM_HK_Diagnostic, msg, (LPARAM)wnd);
146#endif
147 if (!IsWindowVisible(wnd)) return;
148 switch (msg) {
149
150 // -=- Border update events
151 case WM_NCPAINT:
152 case WM_NCACTIVATE:
153 NotifyWindowBorder(wnd, msg);
154 break;
155
156 // -=- Client area update events
157 case BM_SETCHECK:
158 case BM_SETSTATE:
159 case EM_SETSEL:
160 case WM_CHAR:
161 case WM_ENABLE:
162 case WM_KEYUP:
163 case WM_LBUTTONUP:
164 case WM_MBUTTONUP:
165 case WM_PALETTECHANGED:
166 case WM_RBUTTONUP:
167 case WM_SYSCOLORCHANGE:
168 case WM_SETTEXT:
169 case WM_SETFOCUS:
170 //case WM_TIMER:
171 NotifyWindowClientArea(wnd, msg);
172 break;
173 case WM_HSCROLL:
174 case WM_VSCROLL:
175 if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
176 NotifyWindow(wnd, msg);
177 break;
178
179 case WM_WINDOWPOSCHANGING:
180 case WM_DESTROY:
181 {
182 RECT wrect;
183 if (GetWindowRect(wnd, &wrect)) {
184 NotifyRectangle(&wrect);
185 }
186 }
187 break;
188
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +0000189 case WM_WINDOWPOSCHANGED:
190 NotifyWindow(wnd, msg);
191 break;
192
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000193 case WM_PAINT:
194 // *** could improve this
195 NotifyWindowClientArea(wnd, msg);
196 break;
197
198 // Handle pop-up menus appearing
199 case 482:
200 NotifyWindow(wnd, 482);
201 break;
202
203 // Handle pop-up menus having items selected
204 case 485:
205 {
206 HANDLE prop = GetProp(wnd, (LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0));
207 if (prop != (HANDLE) wParam) {
208 NotifyWindow(wnd, 485);
209 SetProp(wnd,
210 (LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0),
211 (HANDLE) wParam);
212 }
213 }
214 break;
215
216 case WM_NCMOUSEMOVE:
217 case WM_MOUSEMOVE:
218 if (enable_cursor_shape) {
219 HCURSOR new_cursor = GetCursor();
220 if (new_cursor != cursor) {
221 cursor = new_cursor;
222 NotifyCursor(cursor);
223 }
224 }
225 break;
226
227 /* ***
228 if (prf_use_GetUpdateRect)
229 {
230 HRGN region;
231 region = CreateRectRgn(0, 0, 0, 0);
232
233 // Get the affected region
234 if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
235 {
236 int buffsize;
237 UINT x;
238 RGNDATA *buff;
239 POINT TopLeft;
240
241 // Get the top-left point of the client area
242 TopLeft.x = 0;
243 TopLeft.y = 0;
244 if (!ClientToScreen(hWnd, &TopLeft))
245 break;
246
247 // Get the size of buffer required
248 buffsize = GetRegionData(region, 0, 0);
249 if (buffsize != 0)
250 {
251 buff = (RGNDATA *) new BYTE [buffsize];
252 if (buff == NULL)
253 break;
254
255 // Now get the region data
256 if(GetRegionData(region, buffsize, buff))
257 {
258 for (x=0; x<(buff->rdh.nCount); x++)
259 {
260 // Obtain the rectangles from the list
261 RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
262 SendDeferredUpdateRect(
263 hWnd,
264 (SHORT) (TopLeft.x + urect->left),
265 (SHORT) (TopLeft.y + urect->top),
266 (SHORT) (TopLeft.x + urect->right),
267 (SHORT) (TopLeft.y + urect->bottom)
268 );
269 }
270 }
271
272 delete [] buff;
273 }
274 }
275
276 // Now free the region
277 if (region != NULL)
278 DeleteObject(region);
279 }
280 */
281 };
282}
283
284LRESULT CALLBACK HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
285 if (nCode == HC_ACTION) {
286 CWPSTRUCT* info = (CWPSTRUCT*) lParam;
287 ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
288 }
289 return CallNextHookEx(hook_CallWndProc, nCode, wParam, lParam);
290}
291
292LRESULT CALLBACK HookCallWndProcRet(int nCode, WPARAM wParam, LPARAM lParam) {
293 if (nCode == HC_ACTION) {
294 CWPRETSTRUCT* info = (CWPRETSTRUCT*) lParam;
295 ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
296 }
297 return CallNextHookEx(hook_CallWndProcRet, nCode, wParam, lParam);
298}
299
300LRESULT CALLBACK HookGetMessage(int nCode, WPARAM wParam, LPARAM lParam) {
301 if (nCode == HC_ACTION) {
302 if (wParam & PM_REMOVE) {
303 MSG* msg = (MSG*) lParam;
304 ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
305 }
306 }
307 return CallNextHookEx(hook_GetMessage, nCode, wParam, lParam);
308}
309
310LRESULT CALLBACK HookDialogMessage(int nCode, WPARAM wParam, LPARAM lParam) {
311 if (nCode == HC_ACTION) {
312 MSG* msg = (MSG*) lParam;
313 ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
314 }
315 return CallNextHookEx(hook_DialogMessage, nCode, wParam, lParam);
316}
317
318// - WM_Hooks_Install
319
320BOOL WM_Hooks_Install(DWORD owner, DWORD thread) {
321 // - Are there already hooks set?
322 if (hook_owner) {
323 if (!PostThreadMessage(hook_owner, WM_HK_PingThread, 0, 0)) {
324 WM_Hooks_Remove(hook_owner);
325 } else {
326 return FALSE;
327 }
328 }
329
330 // - Initialise the hooks
331 hook_owner = owner;
332 hook_target = thread;
333
334 hook_CallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, dll_instance, thread);
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +0000335 hook_CallWndProcRet = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProcRet, dll_instance, thread);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000336 hook_GetMessage = SetWindowsHookEx(WH_GETMESSAGE, HookGetMessage, dll_instance, thread);
337 hook_DialogMessage = SetWindowsHookEx(WH_SYSMSGFILTER, HookDialogMessage, dll_instance, thread);
338
339 if (!hook_CallWndProc /*|| !hook_CallWndProcRet*/ || !hook_GetMessage || !hook_DialogMessage) {
340 WM_Hooks_Remove(owner);
341 return FALSE;
342 }
343
344 return TRUE;
345}
346
347// - WM_Hooks_Remove
348
349BOOL WM_Hooks_Remove(DWORD owner) {
350 if (owner != hook_owner) return FALSE;
351 if (hook_CallWndProc) {
352 UnhookWindowsHookEx(hook_CallWndProc);
353 hook_CallWndProc = 0;
354 }
355 if (hook_CallWndProcRet) {
356 UnhookWindowsHookEx(hook_CallWndProcRet);
357 hook_CallWndProcRet = 0;
358 }
359 if (hook_GetMessage) {
360 UnhookWindowsHookEx(hook_GetMessage);
361 hook_GetMessage = 0;
362 }
363 if (hook_DialogMessage) {
364 UnhookWindowsHookEx(hook_DialogMessage);
365 hook_DialogMessage = 0;
366 }
367 hook_owner = 0;
368 hook_target = 0;
369 return TRUE;
370}
371
372//
373// -=- User input hooks
374//
375
376#pragma data_seg(".WM_Hooks_Shared")
377HHOOK hook_keyboard = 0;
378HHOOK hook_pointer = 0;
379bool enable_real_ptr = true;
380bool enable_synth_ptr = true;
381bool enable_real_kbd = true;
382bool enable_synth_kbd = true;
383#pragma data_seg()
384
385#ifdef WH_KEYBOARD_LL
386LRESULT CALLBACK HookKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) {
387 if (nCode >= 0) {
388 KBDLLHOOKSTRUCT* info = (KBDLLHOOKSTRUCT*) lParam;
389 bool real_event = (info->flags & LLKHF_INJECTED) == 0;
390 if ((real_event && !enable_real_kbd) ||
391 (!real_event && !enable_synth_kbd)) {
392 return 1;
393 }
394 }
395 return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
396}
397
398LRESULT CALLBACK HookPointerHook(int nCode, WPARAM wParam, LPARAM lParam) {
399 if (nCode >= 0) {
400 MSLLHOOKSTRUCT* info = (MSLLHOOKSTRUCT*) lParam;
401 bool real_event = (info->flags & LLMHF_INJECTED) == 0;
402 if ((real_event && !enable_real_ptr) ||
403 (!real_event && !enable_synth_ptr)) {
404 return 1;
405 }
406 }
407 return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
408}
409
410bool RefreshInputHooks() {
411 bool success = true;
412 bool set_ptr_hook = !enable_real_ptr || !enable_synth_ptr;
413 bool set_kbd_hook = !enable_real_kbd || !enable_synth_kbd;
414 if (hook_keyboard && !set_kbd_hook) {
415 UnhookWindowsHookEx(hook_keyboard);
416 hook_keyboard = 0;
417 }
418 if (hook_pointer && !set_ptr_hook) {
419 UnhookWindowsHookEx(hook_pointer);
420 hook_pointer = 0;
421 }
422 if (!hook_keyboard && set_kbd_hook) {
423 hook_keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboardHook, dll_instance, 0);
424 if (!hook_keyboard) success = false;
425 }
426 if (!hook_pointer && set_ptr_hook) {
427 hook_pointer = SetWindowsHookEx(WH_MOUSE_LL, HookPointerHook, dll_instance, 0);
428 if (!hook_pointer) success = false;
429 }
430 return success;
431}
432#else
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +0000433#pragma message(" NOTE: low-level mouse and keyboard hooks not supported")
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000434#endif
435
436// - WM_Hooks_EnableRealInputs
437
438BOOL WM_Hooks_EnableRealInputs(BOOL pointer, BOOL keyboard) {
439#ifdef WH_KEYBOARD_LL
440 enable_real_ptr = pointer!=0;
441 enable_real_kbd = keyboard!=0;
442 return RefreshInputHooks();
443#else
444 return FALSE;
445#endif
446}
447
448// - WM_Hooks_EnableSynthInputs
449
450BOOL WM_Hooks_EnableSynthInputs(BOOL pointer, BOOL keyboard) {
451#ifdef WH_KEYBOARD_LL
452 enable_synth_ptr = pointer!=0;
453 enable_synth_kbd = keyboard!=0;
454 return RefreshInputHooks();
455#else
456 return FALSE;
457#endif
458}
459
460// - WM_Hooks_EnableCursorShape
461
462BOOL WM_Hooks_EnableCursorShape(BOOL enable) {
463 enable_cursor_shape = enable;
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +0000464 return TRUE;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000465}