CmPlatformImpl.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. #include "CmPlatform.h"
  2. #include "CmRenderWindow.h"
  3. #include "CmPixelUtil.h"
  4. #include "CmApplication.h"
  5. #include "CmWin32Defs.h"
  6. #include "CmDebug.h"
  7. #include "Win32/CmWin32DropTarget.h"
  8. namespace CamelotFramework
  9. {
  10. boost::signal<void(RenderWindow*)> Platform::onMouseLeftWindow;
  11. boost::signal<void(const Vector2I&, OSPositionalInputButtonStates)> Platform::onCursorMoved;
  12. boost::signal<void(const Vector2I&, OSMouseButton button, OSPositionalInputButtonStates)> Platform::onCursorButtonPressed;
  13. boost::signal<void(const Vector2I&, OSMouseButton button, OSPositionalInputButtonStates)> Platform::onCursorButtonReleased;
  14. boost::signal<void(const Vector2I&, OSPositionalInputButtonStates)> Platform::onCursorDoubleClick;
  15. boost::signal<void(InputCommandType)> Platform::onInputCommand;
  16. boost::signal<void(float)> Platform::onMouseWheelScrolled;
  17. boost::signal<void(UINT32)> Platform::onCharInput;
  18. boost::signal<void(RenderWindow*)> Platform::onWindowFocusReceived;
  19. boost::signal<void(RenderWindow*)> Platform::onWindowFocusLost;
  20. boost::signal<void(RenderWindow*)> Platform::onWindowMovedOrResized;
  21. boost::signal<void()> Platform::onMouseCaptureChanged;
  22. Map<const RenderWindow*, WindowNonClientAreaData>::type Platform::mNonClientAreas;
  23. bool Platform::mIsTrackingMouse = false;
  24. Vector<RenderWindow*>::type Platform::mMouseLeftWindows;
  25. Stack<RenderWindow*>::type Platform::mModalWindowStack;
  26. NativeDropTargetData Platform::mDropTargets;
  27. bool Platform::mRequiresStartUp = false;
  28. bool Platform::mRequiresShutDown = false;
  29. CM_STATIC_MUTEX_CLASS_INSTANCE(mSync, Platform);
  30. struct NativeCursorData::Pimpl
  31. {
  32. HCURSOR cursor;
  33. };
  34. NativeCursorData::NativeCursorData()
  35. {
  36. data = cm_new<Pimpl>();
  37. }
  38. NativeCursorData::~NativeCursorData()
  39. {
  40. cm_delete(data);
  41. }
  42. struct NativeDropTargetData::Pimpl
  43. {
  44. Map<const RenderWindow*, Win32DropTarget*>::type dropTargetsPerWindow;
  45. Vector<Win32DropTarget*>::type dropTargetsToInitialize;
  46. Vector<Win32DropTarget*>::type dropTargetsToDestroy;
  47. };
  48. NativeDropTargetData::NativeDropTargetData()
  49. {
  50. data = cm_new<Pimpl>();
  51. }
  52. NativeDropTargetData::~NativeDropTargetData()
  53. {
  54. cm_delete(data);
  55. }
  56. bool Platform::mIsCursorHidden = false;
  57. NativeCursorData Platform::mCursor;
  58. bool Platform::mUsingCustomCursor = false;
  59. void Platform::setCursorPosition(const Vector2I& screenPos)
  60. {
  61. SetCursorPos(screenPos.x, screenPos.y);
  62. }
  63. void Platform::captureMouse(const RenderWindow& window)
  64. {
  65. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow();
  66. HWND hwnd;
  67. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  68. PostMessage(hwnd, WM_CM_SETCAPTURE, WPARAM(hwnd), 0);
  69. }
  70. void Platform::releaseMouseCapture()
  71. {
  72. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow();
  73. HWND hwnd;
  74. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  75. PostMessage(hwnd, WM_CM_RELEASECAPTURE, WPARAM(hwnd), 0);
  76. }
  77. bool Platform::isPointOverWindow(const RenderWindow& window, const Vector2I& screenPos)
  78. {
  79. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow();
  80. POINT point;
  81. point.x = screenPos.x;
  82. point.y = screenPos.y;
  83. HWND hwndToCheck;
  84. window.getCustomAttribute("WINDOW", &hwndToCheck);
  85. HWND hwndUnderPos = WindowFromPoint(point);
  86. return hwndUnderPos == hwndToCheck;
  87. }
  88. void Platform::hideCursor()
  89. {
  90. mIsCursorHidden = true;
  91. // ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using
  92. // WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway.
  93. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow();
  94. HWND hwnd;
  95. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  96. PostMessage(hwnd, WM_SETCURSOR, WPARAM(hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
  97. }
  98. void Platform::showCursor()
  99. {
  100. mIsCursorHidden = false;
  101. // ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using
  102. // WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway.
  103. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow();
  104. HWND hwnd;
  105. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  106. PostMessage(hwnd, WM_SETCURSOR, WPARAM(hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
  107. }
  108. void Platform::clipCursorToWindow(const RenderWindow& window)
  109. {
  110. HWND hwnd;
  111. window.getCustomAttribute("WINDOW", &hwnd);
  112. // Clip cursor to the window
  113. RECT clipWindowRect;
  114. if(GetWindowRect(hwnd, &clipWindowRect))
  115. {
  116. ClipCursor(&clipWindowRect);
  117. }
  118. }
  119. void Platform::clipCursorToRect(const RectI& screenRect)
  120. {
  121. RECT clipWindowRect;
  122. clipWindowRect.left = screenRect.x;
  123. clipWindowRect.top = screenRect.y;
  124. clipWindowRect.right = screenRect.x + screenRect.width;
  125. clipWindowRect.bottom = screenRect.y + screenRect.height;
  126. ClipCursor(&clipWindowRect);
  127. }
  128. void Platform::clipCursorDisable()
  129. {
  130. ClipCursor(NULL);
  131. }
  132. void Platform::setCursor(CursorType type)
  133. {
  134. if(mUsingCustomCursor)
  135. {
  136. SetCursor(0);
  137. DestroyIcon(mCursor.data->cursor);
  138. mUsingCustomCursor = false;
  139. }
  140. switch(type)
  141. {
  142. case CursorType::Arrow:
  143. mCursor.data->cursor = LoadCursor(0, IDC_ARROW);
  144. break;
  145. case CursorType::Wait:
  146. mCursor.data->cursor = LoadCursor(0, IDC_WAIT);
  147. break;
  148. case CursorType::IBeam:
  149. mCursor.data->cursor = LoadCursor(0, IDC_IBEAM);
  150. break;
  151. case CursorType::Help:
  152. mCursor.data->cursor = LoadCursor(0, IDC_HELP);
  153. break;
  154. case CursorType::Hand:
  155. mCursor.data->cursor = LoadCursor(0, IDC_HAND);
  156. break;
  157. case CursorType::SizeAll:
  158. mCursor.data->cursor = LoadCursor(0, IDC_SIZEALL);
  159. break;
  160. case CursorType::SizeNESW:
  161. mCursor.data->cursor = LoadCursor(0, IDC_SIZENESW);
  162. break;
  163. case CursorType::SizeNS:
  164. mCursor.data->cursor = LoadCursor(0, IDC_SIZENS);
  165. break;
  166. case CursorType::SizeNWSE:
  167. mCursor.data->cursor = LoadCursor(0, IDC_SIZENWSE);
  168. break;
  169. case CursorType::SizeWE:
  170. mCursor.data->cursor = LoadCursor(0, IDC_SIZEWE);
  171. break;
  172. }
  173. // Make sure we notify the message loop to perform the actual cursor update
  174. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow();
  175. HWND hwnd;
  176. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  177. PostMessage(hwnd, WM_SETCURSOR, WPARAM(hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
  178. }
  179. // TODO - Add support for animated custom cursor
  180. void Platform::setCustomCursor(PixelData& pixelData, const Vector2I& hotSpot)
  181. {
  182. if(mUsingCustomCursor)
  183. {
  184. SetCursor(0);
  185. DestroyIcon(mCursor.data->cursor);
  186. }
  187. mUsingCustomCursor = true;
  188. BITMAPV5HEADER bi;
  189. ZeroMemory(&bi,sizeof(BITMAPV5HEADER));
  190. bi.bV5Size = sizeof(BITMAPV5HEADER);
  191. bi.bV5Width = pixelData.getWidth();
  192. bi.bV5Height = pixelData.getHeight();
  193. bi.bV5Planes = 1;
  194. bi.bV5BitCount = 32;
  195. bi.bV5Compression = BI_BITFIELDS;
  196. bi.bV5RedMask = 0x00FF0000;
  197. bi.bV5GreenMask = 0x0000FF00;
  198. bi.bV5BlueMask = 0x000000FF;
  199. bi.bV5AlphaMask = 0xFF000000;
  200. HDC hDC = GetDC(NULL);
  201. void* data = nullptr;
  202. HBITMAP hBitmap = CreateDIBSection(hDC, (BITMAPINFO *)&bi, DIB_RGB_COLORS,
  203. (void**)&data, NULL, (DWORD)0);
  204. HDC hBitmapDC = CreateCompatibleDC(hDC);
  205. ReleaseDC(NULL, hDC);
  206. // Create an empty mask bitmap.
  207. HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, NULL);
  208. //Select the bitmaps to DC
  209. HBITMAP hOldBitmap = (HBITMAP)SelectObject(hBitmapDC, hBitmap);
  210. //Scan each pixel of the source bitmap and create the masks
  211. Color pixel;
  212. DWORD *dst = (DWORD*)data;
  213. for(UINT32 y = 0; y < pixelData.getHeight(); ++y)
  214. {
  215. for(UINT32 x = 0; x < pixelData.getWidth(); ++x)
  216. {
  217. pixel = pixelData.getColorAt(x, pixelData.getHeight() - y - 1);
  218. *dst = pixel.getAsBGRA();
  219. dst++;
  220. }
  221. }
  222. SelectObject(hBitmapDC, hOldBitmap);
  223. DeleteDC(hBitmapDC);
  224. ICONINFO iconinfo = {0};
  225. iconinfo.fIcon = FALSE;
  226. iconinfo.xHotspot = (DWORD)hotSpot.x;
  227. iconinfo.yHotspot = (DWORD)hotSpot.y;
  228. iconinfo.hbmMask = hMonoBitmap;
  229. iconinfo.hbmColor = hBitmap;
  230. mCursor.data->cursor = CreateIconIndirect(&iconinfo);
  231. DeleteObject(hBitmap);
  232. DeleteObject(hMonoBitmap);
  233. // Make sure we notify the message loop to perform the actual cursor update
  234. RenderWindowPtr primaryWindow = gApplication().getPrimaryWindow();
  235. HWND hwnd;
  236. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  237. PostMessage(hwnd, WM_SETCURSOR, WPARAM(hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
  238. }
  239. void Platform::setCaptionNonClientAreas(const RenderWindow& window, const Vector<RectI>::type& nonClientAreas)
  240. {
  241. CM_LOCK_MUTEX(mSync);
  242. mNonClientAreas[&window].moveAreas = nonClientAreas;
  243. }
  244. void Platform::setResizeNonClientAreas(const RenderWindow& window, const Vector<NonClientResizeArea>::type& nonClientAreas)
  245. {
  246. CM_LOCK_MUTEX(mSync);
  247. mNonClientAreas[&window].resizeAreas = nonClientAreas;
  248. }
  249. void Platform::resetNonClientAreas(const RenderWindow& window)
  250. {
  251. CM_LOCK_MUTEX(mSync);
  252. auto iterFind = mNonClientAreas.find(&window);
  253. if(iterFind != end(mNonClientAreas))
  254. mNonClientAreas.erase(iterFind);
  255. }
  256. void Platform::copyToClipboard(const WString& string)
  257. {
  258. HANDLE hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (string.size() + 1) * sizeof(WString::value_type));
  259. WString::value_type* buffer = (WString::value_type*)GlobalLock(hData);
  260. string.copy(buffer, string.size());
  261. buffer[string.size()] = '\0';
  262. GlobalUnlock(hData);
  263. if(OpenClipboard(NULL))
  264. {
  265. EmptyClipboard();
  266. SetClipboardData(CF_UNICODETEXT, hData);
  267. CloseClipboard();
  268. }
  269. else
  270. {
  271. GlobalFree(hData);
  272. }
  273. }
  274. WString Platform::copyFromClipboard()
  275. {
  276. if(OpenClipboard(NULL))
  277. {
  278. HANDLE hData = GetClipboardData(CF_UNICODETEXT);
  279. if(hData != NULL)
  280. {
  281. WString::value_type* buffer = (WString::value_type*)GlobalLock(hData);
  282. WString string(buffer);
  283. GlobalUnlock(hData);
  284. CloseClipboard();
  285. return string;
  286. }
  287. else
  288. {
  289. CloseClipboard();
  290. return L"";
  291. }
  292. }
  293. return L"";
  294. }
  295. double Platform::queryPerformanceTimerMs()
  296. {
  297. LARGE_INTEGER counterValue;
  298. QueryPerformanceCounter(&counterValue);
  299. LARGE_INTEGER counterFreq;
  300. QueryPerformanceFrequency(&counterFreq);
  301. return (double)counterValue.QuadPart / (counterFreq.QuadPart * 0.001);
  302. }
  303. OSDropTarget& Platform::createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height)
  304. {
  305. Win32DropTarget* win32DropTarget = nullptr;
  306. auto iterFind = mDropTargets.data->dropTargetsPerWindow.find(window);
  307. if(iterFind == mDropTargets.data->dropTargetsPerWindow.end())
  308. {
  309. HWND hwnd;
  310. window->getCustomAttribute("WINDOW", &hwnd);
  311. win32DropTarget = cm_new<Win32DropTarget>(hwnd);
  312. mDropTargets.data->dropTargetsPerWindow[window] = win32DropTarget;
  313. {
  314. CM_LOCK_MUTEX(mSync);
  315. mDropTargets.data->dropTargetsToInitialize.push_back(win32DropTarget);
  316. }
  317. }
  318. else
  319. win32DropTarget = iterFind->second;
  320. OSDropTarget* newDropTarget = new (cm_alloc<OSDropTarget>()) OSDropTarget(window, x, y, width, height);
  321. win32DropTarget->registerDropTarget(newDropTarget);
  322. return *newDropTarget;
  323. }
  324. void Platform::destroyDropTarget(OSDropTarget& target)
  325. {
  326. auto iterFind = mDropTargets.data->dropTargetsPerWindow.find(target.getOwnerWindow());
  327. if(iterFind == mDropTargets.data->dropTargetsPerWindow.end())
  328. {
  329. LOGWRN("Attempting to destroy a drop target but cannot find its parent window.");
  330. }
  331. else
  332. {
  333. Win32DropTarget* win32DropTarget = iterFind->second;
  334. win32DropTarget->unregisterDropTarget(&target);
  335. if(win32DropTarget->getNumDropTargets() == 0)
  336. {
  337. mDropTargets.data->dropTargetsPerWindow.erase(iterFind);
  338. {
  339. CM_LOCK_MUTEX(mSync);
  340. mDropTargets.data->dropTargetsToDestroy.push_back(win32DropTarget);
  341. }
  342. }
  343. }
  344. CM_PVT_DELETE(OSDropTarget, &target);
  345. }
  346. void Platform::messagePump()
  347. {
  348. MSG msg;
  349. while(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
  350. {
  351. TranslateMessage(&msg);
  352. DispatchMessage(&msg);
  353. }
  354. }
  355. void Platform::startUp()
  356. {
  357. CM_LOCK_MUTEX(mSync);
  358. mRequiresStartUp = true;
  359. }
  360. void Platform::update()
  361. {
  362. Vector<RenderWindow*>::type windowsCopy;
  363. {
  364. CM_LOCK_MUTEX(mSync);
  365. windowsCopy = mMouseLeftWindows;
  366. mMouseLeftWindows.clear();
  367. }
  368. for(auto& window : windowsCopy)
  369. {
  370. if(!onMouseLeftWindow.empty())
  371. onMouseLeftWindow(window);
  372. }
  373. for(auto& dropTarget : mDropTargets.data->dropTargetsPerWindow)
  374. {
  375. dropTarget.second->update();
  376. }
  377. }
  378. void Platform::coreUpdate()
  379. {
  380. {
  381. CM_LOCK_MUTEX(mSync);
  382. if(mRequiresStartUp)
  383. {
  384. OleInitialize(nullptr);
  385. mRequiresStartUp = false;
  386. }
  387. }
  388. {
  389. CM_LOCK_MUTEX(mSync);
  390. for(auto& dropTargetToInit : mDropTargets.data->dropTargetsToInitialize)
  391. {
  392. dropTargetToInit->registerWithOS();
  393. }
  394. mDropTargets.data->dropTargetsToInitialize.clear();
  395. }
  396. {
  397. CM_LOCK_MUTEX(mSync);
  398. for(auto& dropTargetToDestroy : mDropTargets.data->dropTargetsToDestroy)
  399. {
  400. dropTargetToDestroy->unregisterWithOS();
  401. dropTargetToDestroy->Release();
  402. }
  403. mDropTargets.data->dropTargetsToDestroy.clear();
  404. }
  405. messagePump();
  406. {
  407. CM_LOCK_MUTEX(mSync);
  408. if(mRequiresShutDown)
  409. {
  410. OleUninitialize();
  411. mRequiresShutDown = false;
  412. }
  413. }
  414. }
  415. void Platform::shutDown()
  416. {
  417. CM_LOCK_MUTEX(mSync);
  418. mRequiresShutDown = true;
  419. }
  420. void Platform::windowFocusReceived(RenderWindow* window)
  421. {
  422. if(!onWindowFocusReceived.empty())
  423. onWindowFocusReceived(window);
  424. }
  425. void Platform::windowFocusLost(RenderWindow* window)
  426. {
  427. if(!onWindowFocusLost.empty())
  428. onWindowFocusLost(window);
  429. }
  430. void Platform::windowMovedOrResized(RenderWindow* window)
  431. {
  432. if(!onWindowMovedOrResized.empty())
  433. onWindowMovedOrResized(window);
  434. }
  435. void Platform::win32ShowCursor()
  436. {
  437. SetCursor(mCursor.data->cursor);
  438. }
  439. void Platform::win32HideCursor()
  440. {
  441. SetCursor(nullptr);
  442. }
  443. }