blob: 26a2363cc25eacd4bf9c06c335c01a5775e0b1f5 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
2 *
3 * 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// -=- WMHooks.cxx
20
21#include <wm_hooks/wm_hooks.h>
22
23#include <rfb_win32/WMHooks.h>
24#include <rfb_win32/Service.h>
25#include <rfb/Threading.h>
26#include <rfb/LogWriter.h>
27
28#include <list>
29
30using namespace rfb;
31using namespace rfb::win32;
32
33static LogWriter vlog("WMHooks");
34
35class WMHooksThread : public Thread {
36public:
37 WMHooksThread() : Thread("WMHookThread"), active(true) {}
38 virtual void run();
39 virtual Thread* join();
40protected:
41 bool active;
42};
43
44WMHooksThread* hook_mgr = 0;
45std::list<WMHooks*> hooks;
46std::list<WMCursorHooks*> cursor_hooks;
47Mutex hook_mgr_lock;
48HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
49
50
51bool
52StartHookThread() {
53 if (hook_mgr) return true;
54 vlog.debug("opening hook thread");
55 hook_mgr = new WMHooksThread();
56 if (!WM_Hooks_Install(hook_mgr->getThreadId(), 0)) {
57 vlog.error("failed to initialise hooks");
58 delete hook_mgr->join();
59 hook_mgr = 0;
60 return false;
61 }
62 hook_mgr->start();
63 return true;
64}
65
66void
67StopHookThread() {
68 if (!hook_mgr) return;
69 if (!hooks.empty() || !cursor_hooks.empty()) return;
70 vlog.debug("closing hook thread");
71 delete hook_mgr->join();
72 hook_mgr = 0;
73}
74
75
76bool
77AddHook(WMHooks* hook) {
78 vlog.debug("adding hook");
79 Lock l(hook_mgr_lock);
80 if (!StartHookThread()) return false;
81 hooks.push_back(hook);
82 return true;
83}
84
85bool
86AddCursorHook(WMCursorHooks* hook) {
87 vlog.debug("adding cursor hook");
88 Lock l(hook_mgr_lock);
89 if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(TRUE);
90 if (!StartHookThread()) return false;
91 cursor_hooks.push_back(hook);
92 return true;
93}
94
95bool
96RemHook(WMHooks* hook) {
97 {
98 vlog.debug("removing hook");
99 Lock l(hook_mgr_lock);
100 hooks.remove(hook);
101 }
102 StopHookThread();
103 return true;
104}
105
106bool
107RemCursorHook(WMCursorHooks* hook) {
108 {
109 vlog.debug("removing cursor hook");
110 Lock l(hook_mgr_lock);
111 cursor_hooks.remove(hook);
112 }
113 StopHookThread();
114 if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(FALSE);
115 return true;
116}
117
118void
119NotifyHooksRegion(const Region& r) {
120 Lock l(hook_mgr_lock);
121 std::list<WMHooks*>::iterator i;
122 for (i=hooks.begin(); i!=hooks.end(); i++) {
123 (*i)->new_changes.add_changed(r);
124 if (!(*i)->notified) {
125 (*i)->notified = true;
126 PostMessage((*i)->getHandle(), WM_USER, 0, 0);
127 }
128 }
129}
130
131void
132NotifyHooksCursor(HCURSOR c) {
133 Lock l(hook_mgr_lock);
134 hook_cursor = c;
135}
136
137void
138WMHooksThread::run() {
139 UINT windowMsg = WM_Hooks_WindowChanged();
140 UINT clientAreaMsg = WM_Hooks_WindowClientAreaChanged();
141 UINT borderMsg = WM_Hooks_WindowBorderChanged();
142 UINT rectangleMsg = WM_Hooks_RectangleChanged();
143 UINT cursorMsg = WM_Hooks_CursorChanged();
144#ifdef _DEBUG
145 UINT diagnosticMsg = WM_Hooks_Diagnostic();
146#endif
147 MSG msg;
148 RECT wrect;
149 HWND hwnd;
150 int count = 0;
151
152 vlog.debug("starting hook thread");
153
154 while (active && GetMessage(&msg, NULL, 0, 0)) {
155 count++;
156 if (msg.message == windowMsg) {
157 hwnd = (HWND) msg.lParam;
158 if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
159 GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
160 {
161 NotifyHooksRegion(Rect(wrect.left, wrect.top,
162 wrect.right, wrect.bottom));
163
164 }
165 } else if (msg.message == clientAreaMsg) {
166 hwnd = (HWND) msg.lParam;
167 if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
168 GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
169 {
170 POINT pt = {0,0};
171 if (ClientToScreen(hwnd, &pt)) {
172 NotifyHooksRegion(Rect(wrect.left+pt.x, wrect.top+pt.y,
173 wrect.right+pt.x, wrect.bottom+pt.y));
174 }
175 }
176 } else if (msg.message == borderMsg) {
177 hwnd = (HWND) msg.lParam;
178 if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
179 GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
180 {
181 Region changed(Rect(wrect.left, wrect.top, wrect.right, wrect.bottom));
182 RECT crect;
183 POINT pt = {0,0};
184 if (GetClientRect(hwnd, &crect) && ClientToScreen(hwnd, &pt) &&
185 !IsRectEmpty(&crect))
186 {
187 changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y,
188 crect.right+pt.x, crect.bottom+pt.y));
189 }
190 NotifyHooksRegion(changed);
191 }
192 } else if (msg.message == rectangleMsg) {
193 Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam),
194 LOWORD(msg.lParam), HIWORD(msg.lParam));
195 if (!r.is_empty()) {
196 NotifyHooksRegion(r);
197 }
198 } else if (msg.message == cursorMsg) {
199 NotifyHooksCursor((HCURSOR)msg.lParam);
200#ifdef _DEBUG
201 } else if (msg.message == diagnosticMsg) {
202 vlog.info("DIAG msg=%x(%d) wnd=%lx", msg.wParam, msg.wParam, msg.lParam);
203#endif
204 }
205 }
206
207 vlog.debug("stopping hook thread - processed %d events", count);
208 WM_Hooks_Remove(getThreadId());
209}
210
211Thread*
212WMHooksThread::join() {
213 vlog.debug("stopping WMHooks thread");
214 active = false;
215 PostThreadMessage(thread_id, WM_QUIT, 0, 0);
216 vlog.debug("joining WMHooks thread");
217 return Thread::join();
218}
219
220// -=- WMHooks class
221
222rfb::win32::WMHooks::WMHooks()
223 : clipper(0), new_changes(true), fg_window(0),
224 notified(false), MsgWindow(_T("WMHooks")) {
225}
226
227rfb::win32::WMHooks::~WMHooks() {
228 RemHook(this);
229 if (clipper) delete clipper;
230}
231
232LRESULT
233rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
234 switch (msg) {
235 case WM_USER:
236 {
237 // *** Yield, to allow the triggering update event to be processed
238 // BEFORE we try to grab the resulting changes.
239 // *** IMPROVES THINGS NOTICABLY ON WinXP
240 Sleep(0);
241 // ***
242
243 Lock l(hook_mgr_lock);
244 notified = false;
245 new_changes.get_update(*clipper);
246 new_changes.clear();
247 }
248 break;
249 }
250 return MsgWindow::processMessage(msg, wParam, lParam);
251}
252
253bool
254rfb::win32::WMHooks::setClipRect(const Rect& r) {
255 clip_region = r;
256 if (clipper) clipper->set_clip_region(clip_region);
257 return true;
258}
259
260bool
261rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {
262 if (clipper) delete clipper;
263 clipper = new ClippedUpdateTracker(*ut);
264 clipper->set_clip_region(clip_region);
265 return AddHook(this);
266}
267
268#ifdef _DEBUG
269void
270rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) {
271 WM_Hooks_SetDiagnosticRange(min, max);
272}
273#endif
274
275
276// -=- WMBlockInput class
277
278Mutex blockMutex;
279int blockCount = 0;
280
281rfb::win32::WMBlockInput::WMBlockInput() : active(false) {
282}
283
284rfb::win32::WMBlockInput::~WMBlockInput() {
285 blockInputs(false);
286}
287
288bool rfb::win32::WMBlockInput::blockInputs(bool on) {
289 if (on == active) return true;
290 vlog.debug("blockInput changed");
291 Lock l(blockMutex);
292 int newCount = blockCount;
293 if (on)
294 newCount++;
295 else
296 newCount--;
297 if (WM_Hooks_EnableRealInputs(newCount==0, newCount==0)) {
298 vlog.debug("set blocking to %d", newCount);
299 blockCount = newCount;
300 active = on;
301 return true;
302 }
303 return false;
304}
305
306
307// -=- WMCursorHooks class
308
309rfb::win32::WMCursorHooks::WMCursorHooks() {
310}
311
312rfb::win32::WMCursorHooks::~WMCursorHooks() {
313 RemCursorHook(this);
314}
315
316bool
317rfb::win32::WMCursorHooks::start() {
318 return AddCursorHook(this);
319}
320
321HCURSOR
322rfb::win32::WMCursorHooks::getCursor() const {
323 return hook_cursor;
324}