You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WMHooks.cxx 7.9KB

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