BsWin32Platform.cpp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Win32/BsWin32Platform.h"
  4. #include "BsRenderWindow.h"
  5. #include "BsPixelUtil.h"
  6. #include "BsCoreApplication.h"
  7. #include "BsDebug.h"
  8. #include "BsRenderWindowManager.h"
  9. #include "Win32/BsWin32Defs.h"
  10. #include "Win32/BsWin32DropTarget.h"
  11. #include "Win32/BsWin32PlatformData.h"
  12. #include "TimeAPI.h"
  13. namespace BansheeEngine
  14. {
  15. Event<void(const Vector2I&, OSPointerButtonStates)> Platform::onCursorMoved;
  16. Event<void(const Vector2I&, OSMouseButton button, OSPointerButtonStates)> Platform::onCursorButtonPressed;
  17. Event<void(const Vector2I&, OSMouseButton button, OSPointerButtonStates)> Platform::onCursorButtonReleased;
  18. Event<void(const Vector2I&, OSPointerButtonStates)> Platform::onCursorDoubleClick;
  19. Event<void(InputCommandType)> Platform::onInputCommand;
  20. Event<void(float)> Platform::onMouseWheelScrolled;
  21. Event<void(UINT32)> Platform::onCharInput;
  22. Event<void(RenderWindowCore*)> Platform::onMouseLeftWindow;
  23. Event<void()> Platform::onMouseCaptureChanged;
  24. Platform::Pimpl* Platform::mData = bs_new<Platform::Pimpl>();
  25. Platform::~Platform()
  26. {
  27. bs_delete(mData);
  28. mData = nullptr;
  29. }
  30. Vector2I Platform::getCursorPosition()
  31. {
  32. Vector2I screenPos;
  33. POINT cursorPos;
  34. GetCursorPos(&cursorPos);
  35. screenPos.x = cursorPos.x;
  36. screenPos.y = cursorPos.y;
  37. return screenPos;
  38. }
  39. void Platform::setCursorPosition(const Vector2I& screenPos)
  40. {
  41. SetCursorPos(screenPos.x, screenPos.y);
  42. }
  43. void Platform::captureMouse(const RenderWindow& window)
  44. {
  45. RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
  46. UINT64 hwnd;
  47. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  48. PostMessage((HWND)hwnd, WM_BS_SETCAPTURE, WPARAM((HWND)hwnd), 0);
  49. }
  50. void Platform::releaseMouseCapture()
  51. {
  52. RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
  53. UINT64 hwnd;
  54. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  55. PostMessage((HWND)hwnd, WM_BS_RELEASECAPTURE, WPARAM((HWND)hwnd), 0);
  56. }
  57. bool Platform::isPointOverWindow(const RenderWindow& window, const Vector2I& screenPos)
  58. {
  59. RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
  60. POINT point;
  61. point.x = screenPos.x;
  62. point.y = screenPos.y;
  63. UINT64 hwndToCheck;
  64. window.getCustomAttribute("WINDOW", &hwndToCheck);
  65. HWND hwndUnderPos = WindowFromPoint(point);
  66. return hwndUnderPos == (HWND)hwndToCheck;
  67. }
  68. void Platform::hideCursor()
  69. {
  70. mData->mIsCursorHidden = true;
  71. // ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using
  72. // WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway.
  73. RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
  74. UINT64 hwnd;
  75. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  76. PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
  77. }
  78. void Platform::showCursor()
  79. {
  80. mData->mIsCursorHidden = false;
  81. // ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using
  82. // WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway.
  83. RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
  84. UINT64 hwnd;
  85. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  86. PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
  87. }
  88. bool Platform::isCursorHidden()
  89. {
  90. return mData->mIsCursorHidden;
  91. }
  92. void Platform::clipCursorToWindow(const RenderWindow& window)
  93. {
  94. UINT64 hwnd;
  95. window.getCustomAttribute("WINDOW", &hwnd);
  96. // Clip cursor to the window
  97. RECT clipWindowRect;
  98. if(GetWindowRect((HWND)hwnd, &clipWindowRect))
  99. {
  100. ClipCursor(&clipWindowRect);
  101. }
  102. }
  103. void Platform::clipCursorToRect(const Rect2I& screenRect)
  104. {
  105. RECT clipWindowRect;
  106. clipWindowRect.left = screenRect.x;
  107. clipWindowRect.top = screenRect.y;
  108. clipWindowRect.right = screenRect.x + screenRect.width;
  109. clipWindowRect.bottom = screenRect.y + screenRect.height;
  110. ClipCursor(&clipWindowRect);
  111. }
  112. void Platform::clipCursorDisable()
  113. {
  114. ClipCursor(NULL);
  115. }
  116. // TODO - Add support for animated custom cursor
  117. void Platform::setCursor(PixelData& pixelData, const Vector2I& hotSpot)
  118. {
  119. if (mData->mUsingCustomCursor)
  120. {
  121. SetCursor(0);
  122. DestroyIcon(mData->mCursor.cursor);
  123. }
  124. mData->mUsingCustomCursor = true;
  125. HBITMAP hBitmap = Win32Platform::createBitmap(pixelData, false);
  126. HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, nullptr);
  127. ICONINFO iconinfo = {0};
  128. iconinfo.fIcon = FALSE;
  129. iconinfo.xHotspot = (DWORD)hotSpot.x;
  130. iconinfo.yHotspot = (DWORD)hotSpot.y;
  131. iconinfo.hbmMask = hMonoBitmap;
  132. iconinfo.hbmColor = hBitmap;
  133. mData->mCursor.cursor = CreateIconIndirect(&iconinfo);
  134. DeleteObject(hBitmap);
  135. DeleteObject(hMonoBitmap);
  136. // Make sure we notify the message loop to perform the actual cursor update
  137. RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
  138. UINT64 hwnd;
  139. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  140. PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
  141. }
  142. void Platform::setIcon(const PixelData& pixelData)
  143. {
  144. PixelDataPtr resizedData = PixelData::create(32, 32, 1, PF_R8G8B8A8);
  145. PixelUtil::scale(pixelData, *resizedData);
  146. HBITMAP hBitmap = Win32Platform::createBitmap(pixelData, false);
  147. HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, nullptr);
  148. ICONINFO iconinfo = { 0 };
  149. iconinfo.fIcon = TRUE;
  150. iconinfo.xHotspot = 0;
  151. iconinfo.yHotspot = 0;
  152. iconinfo.hbmMask = hMonoBitmap;
  153. iconinfo.hbmColor = hBitmap;
  154. HICON icon = CreateIconIndirect(&iconinfo);
  155. DeleteObject(hBitmap);
  156. DeleteObject(hMonoBitmap);
  157. // Make sure we notify the message loop to perform the actual cursor update
  158. RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
  159. UINT64 hwnd;
  160. primaryWindow->getCustomAttribute("WINDOW", &hwnd);
  161. PostMessage((HWND)hwnd, WM_SETICON, WPARAM(ICON_BIG), (LPARAM)icon);
  162. }
  163. void Platform::setCaptionNonClientAreas(const RenderWindowCore& window, const Vector<Rect2I>& nonClientAreas)
  164. {
  165. BS_LOCK_MUTEX(mData->mSync);
  166. mData->mNonClientAreas[&window].moveAreas = nonClientAreas;
  167. }
  168. void Platform::setResizeNonClientAreas(const RenderWindowCore& window, const Vector<NonClientResizeArea>& nonClientAreas)
  169. {
  170. BS_LOCK_MUTEX(mData->mSync);
  171. mData->mNonClientAreas[&window].resizeAreas = nonClientAreas;
  172. }
  173. void Platform::resetNonClientAreas(const RenderWindowCore& window)
  174. {
  175. BS_LOCK_MUTEX(mData->mSync);
  176. auto iterFind = mData->mNonClientAreas.find(&window);
  177. if (iterFind != end(mData->mNonClientAreas))
  178. mData->mNonClientAreas.erase(iterFind);
  179. }
  180. void Platform::sleep(UINT32 duration)
  181. {
  182. Sleep((DWORD)duration);
  183. }
  184. OSDropTarget& Platform::createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height)
  185. {
  186. Win32DropTarget* win32DropTarget = nullptr;
  187. auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(window);
  188. if (iterFind == mData->mDropTargets.dropTargetsPerWindow.end())
  189. {
  190. UINT64 hwnd;
  191. window->getCustomAttribute("WINDOW", &hwnd);
  192. win32DropTarget = bs_new<Win32DropTarget>((HWND)hwnd);
  193. mData->mDropTargets.dropTargetsPerWindow[window] = win32DropTarget;
  194. {
  195. BS_LOCK_MUTEX(mData->mSync);
  196. mData->mDropTargets.dropTargetsToInitialize.push_back(win32DropTarget);
  197. }
  198. }
  199. else
  200. win32DropTarget = iterFind->second;
  201. OSDropTarget* newDropTarget = new (bs_alloc<OSDropTarget>()) OSDropTarget(window, x, y, width, height);
  202. win32DropTarget->registerDropTarget(newDropTarget);
  203. return *newDropTarget;
  204. }
  205. void Platform::destroyDropTarget(OSDropTarget& target)
  206. {
  207. auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(target.getOwnerWindow());
  208. if (iterFind == mData->mDropTargets.dropTargetsPerWindow.end())
  209. {
  210. LOGWRN("Attempting to destroy a drop target but cannot find its parent window.");
  211. }
  212. else
  213. {
  214. Win32DropTarget* win32DropTarget = iterFind->second;
  215. win32DropTarget->unregisterDropTarget(&target);
  216. if(win32DropTarget->getNumDropTargets() == 0)
  217. {
  218. mData->mDropTargets.dropTargetsPerWindow.erase(iterFind);
  219. {
  220. BS_LOCK_MUTEX(mData->mSync);
  221. mData->mDropTargets.dropTargetsToDestroy.push_back(win32DropTarget);
  222. }
  223. }
  224. }
  225. BS_PVT_DELETE(OSDropTarget, &target);
  226. }
  227. void Platform::_messagePump()
  228. {
  229. MSG msg;
  230. while (PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
  231. {
  232. TranslateMessage(&msg);
  233. DispatchMessage(&msg);
  234. }
  235. }
  236. void Platform::_startUp()
  237. {
  238. BS_LOCK_MUTEX(mData->mSync);
  239. if (timeBeginPeriod(1) == TIMERR_NOCANDO)
  240. {
  241. LOGWRN("Unable to set timer resolution to 1ms. This can cause significant waste " \
  242. "in performance for waiting threads.");
  243. }
  244. mData->mRequiresStartUp = true;
  245. }
  246. void Platform::_update()
  247. {
  248. for (auto& dropTarget : mData->mDropTargets.dropTargetsPerWindow)
  249. {
  250. dropTarget.second->update();
  251. }
  252. }
  253. void Platform::_coreUpdate()
  254. {
  255. {
  256. BS_LOCK_MUTEX(mData->mSync);
  257. if (mData->mRequiresStartUp)
  258. {
  259. OleInitialize(nullptr);
  260. mData->mRequiresStartUp = false;
  261. }
  262. }
  263. {
  264. BS_LOCK_MUTEX(mData->mSync);
  265. for (auto& dropTargetToDestroy : mData->mDropTargets.dropTargetsToDestroy)
  266. {
  267. dropTargetToDestroy->unregisterWithOS();
  268. dropTargetToDestroy->Release();
  269. }
  270. mData->mDropTargets.dropTargetsToDestroy.clear();
  271. }
  272. {
  273. BS_LOCK_MUTEX(mData->mSync);
  274. for (auto& dropTargetToInit : mData->mDropTargets.dropTargetsToInitialize)
  275. {
  276. dropTargetToInit->registerWithOS();
  277. }
  278. mData->mDropTargets.dropTargetsToInitialize.clear();
  279. }
  280. _messagePump();
  281. {
  282. BS_LOCK_MUTEX(mData->mSync);
  283. if (mData->mRequiresShutDown)
  284. {
  285. OleUninitialize();
  286. mData->mRequiresShutDown = false;
  287. }
  288. }
  289. }
  290. void Platform::_shutDown()
  291. {
  292. BS_LOCK_MUTEX(mData->mSync);
  293. timeEndPeriod(1);
  294. mData->mRequiresShutDown = true;
  295. }
  296. bool isShiftPressed = false;
  297. bool isCtrlPressed = false;
  298. /** Translate engine non client area to win32 non client area. */
  299. LRESULT translateNonClientAreaType(NonClientAreaBorderType type)
  300. {
  301. LRESULT dir = HTCLIENT;
  302. switch(type)
  303. {
  304. case NonClientAreaBorderType::Left:
  305. dir = HTLEFT;
  306. break;
  307. case NonClientAreaBorderType::TopLeft:
  308. dir = HTTOPLEFT;
  309. break;
  310. case NonClientAreaBorderType::Top:
  311. dir = HTTOP;
  312. break;
  313. case NonClientAreaBorderType::TopRight:
  314. dir = HTTOPRIGHT;
  315. break;
  316. case NonClientAreaBorderType::Right:
  317. dir = HTRIGHT;
  318. break;
  319. case NonClientAreaBorderType::BottomRight:
  320. dir = HTBOTTOMRIGHT;
  321. break;
  322. case NonClientAreaBorderType::Bottom:
  323. dir = HTBOTTOM;
  324. break;
  325. case NonClientAreaBorderType::BottomLeft:
  326. dir = HTBOTTOMLEFT;
  327. break;
  328. }
  329. return dir;
  330. }
  331. /** Method triggered whenever a mouse event happens. */
  332. void getMouseData(HWND hWnd, WPARAM wParam, LPARAM lParam, bool nonClient, Vector2I& mousePos, OSPointerButtonStates& btnStates)
  333. {
  334. POINT clientPoint;
  335. clientPoint.x = GET_X_LPARAM(lParam);
  336. clientPoint.y = GET_Y_LPARAM(lParam);
  337. if (!nonClient)
  338. ClientToScreen(hWnd, &clientPoint);
  339. mousePos.x = clientPoint.x;
  340. mousePos.y = clientPoint.y;
  341. btnStates.mouseButtons[0] = (wParam & MK_LBUTTON) != 0;
  342. btnStates.mouseButtons[1] = (wParam & MK_MBUTTON) != 0;
  343. btnStates.mouseButtons[2] = (wParam & MK_RBUTTON) != 0;
  344. btnStates.shift = (wParam & MK_SHIFT) != 0;
  345. btnStates.ctrl = (wParam & MK_CONTROL) != 0;
  346. }
  347. /**
  348. * Converts a virtual key code into an input command, if possible. Returns true if conversion was done.
  349. *
  350. * @param[in] virtualKeyCode Virtual key code to try to translate to a command.
  351. * @param[out] command Input command. Only valid if function returns true.
  352. * @param[in] ignoreMovement If true, then movement keys (up/down/left/right) will be ignored and not considered
  353. * as input commands (useful if you need to parse num keys as numbers and not movement).
  354. */
  355. bool getCommand(unsigned int virtualKeyCode, InputCommandType& command, bool ignoreMovement = false)
  356. {
  357. switch (virtualKeyCode)
  358. {
  359. case VK_LEFT:
  360. command = isShiftPressed ? InputCommandType::SelectLeft : InputCommandType::CursorMoveLeft;
  361. return !ignoreMovement;
  362. case VK_RIGHT:
  363. command = isShiftPressed ? InputCommandType::SelectRight : InputCommandType::CursorMoveRight;
  364. return !ignoreMovement;
  365. case VK_UP:
  366. command = isShiftPressed ? InputCommandType::SelectUp : InputCommandType::CursorMoveUp;
  367. return !ignoreMovement;
  368. case VK_DOWN:
  369. command = isShiftPressed ? InputCommandType::SelectDown : InputCommandType::CursorMoveDown;
  370. return !ignoreMovement;
  371. case VK_ESCAPE:
  372. command = InputCommandType::Escape;
  373. return true;
  374. case VK_RETURN:
  375. command = isShiftPressed ? InputCommandType::Return : InputCommandType::Confirm;
  376. return true;
  377. case VK_BACK:
  378. command = InputCommandType::Backspace;
  379. return true;
  380. case VK_DELETE:
  381. command = InputCommandType::Delete;
  382. return true;
  383. }
  384. return false;
  385. }
  386. HBITMAP Win32Platform::createBitmap(const PixelData& pixelData, bool premultiplyAlpha)
  387. {
  388. BITMAPINFO bi;
  389. ZeroMemory(&bi, sizeof(BITMAPINFO));
  390. bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  391. bi.bmiHeader.biWidth = pixelData.getWidth();
  392. bi.bmiHeader.biHeight = pixelData.getHeight();
  393. bi.bmiHeader.biPlanes = 1;
  394. bi.bmiHeader.biBitCount = 32;
  395. bi.bmiHeader.biCompression = BI_RGB;
  396. HDC hDC = GetDC(nullptr);
  397. void* data = nullptr;
  398. HBITMAP hBitmap = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, (void**)&data, nullptr, 0);
  399. HDC hBitmapDC = CreateCompatibleDC(hDC);
  400. ReleaseDC(nullptr, hDC);
  401. //Select the bitmaps to DC
  402. HBITMAP hOldBitmap = (HBITMAP)SelectObject(hBitmapDC, hBitmap);
  403. //Scan each pixel of the source bitmap and create the masks
  404. Color pixel;
  405. DWORD *dst = (DWORD*)data;
  406. for (UINT32 y = 0; y < pixelData.getHeight(); ++y)
  407. {
  408. for (UINT32 x = 0; x < pixelData.getWidth(); ++x)
  409. {
  410. pixel = pixelData.getColorAt(x, pixelData.getHeight() - y - 1);
  411. if (premultiplyAlpha)
  412. {
  413. pixel.r *= pixel.a;
  414. pixel.g *= pixel.a;
  415. pixel.b *= pixel.a;
  416. }
  417. *dst = pixel.getAsBGRA();
  418. dst++;
  419. }
  420. }
  421. SelectObject(hBitmapDC, hOldBitmap);
  422. DeleteDC(hBitmapDC);
  423. return hBitmap;
  424. }
  425. LRESULT CALLBACK Win32Platform::_win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  426. {
  427. if (uMsg == WM_CREATE)
  428. { // Store pointer to Win32Window in user data area
  429. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)(((LPCREATESTRUCT)lParam)->lpCreateParams));
  430. RenderWindowCore* newWindow = (RenderWindowCore*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
  431. if (newWindow != nullptr)
  432. {
  433. const RenderWindowProperties& props = newWindow->getProperties();
  434. if (!props.isHidden())
  435. ShowWindow(hWnd, SW_SHOWNOACTIVATE);
  436. if (props.isModal())
  437. {
  438. if (!mData->mModalWindowStack.empty())
  439. {
  440. RenderWindowCore* curModalWindow = mData->mModalWindowStack.back();
  441. UINT64 curHwnd;
  442. curModalWindow->getCustomAttribute("WINDOW", &curHwnd);
  443. EnableWindow((HWND)curHwnd, FALSE);
  444. }
  445. else
  446. {
  447. Vector<RenderWindowCore*> renderWindows = RenderWindowCoreManager::instance().getRenderWindows();
  448. for (auto& renderWindow : renderWindows)
  449. {
  450. if (renderWindow == newWindow)
  451. continue;
  452. UINT64 curHwnd;
  453. renderWindow->getCustomAttribute("WINDOW", &curHwnd);
  454. EnableWindow((HWND)curHwnd, FALSE);
  455. }
  456. }
  457. mData->mModalWindowStack.push_back(newWindow);
  458. }
  459. else
  460. {
  461. // A non-modal window was opened while another modal one is open:
  462. // immediately deactivate it and make sure the modal windows stay on top
  463. if (!mData->mModalWindowStack.empty())
  464. {
  465. EnableWindow((HWND)hWnd, FALSE);
  466. for (auto window : mData->mModalWindowStack)
  467. {
  468. UINT64 curHwnd;
  469. window->getCustomAttribute("WINDOW", &curHwnd);
  470. BringWindowToTop((HWND)curHwnd);
  471. }
  472. }
  473. }
  474. }
  475. else
  476. ShowWindow(hWnd, SW_SHOWNOACTIVATE);
  477. return 0;
  478. }
  479. // look up window instance
  480. // note: it is possible to get a WM_SIZE before WM_CREATE
  481. RenderWindowCore* win = (RenderWindowCore*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
  482. if (!win)
  483. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  484. switch( uMsg )
  485. {
  486. case WM_DESTROY:
  487. {
  488. bool reenableWindows = false;
  489. if (!mData->mModalWindowStack.empty())
  490. {
  491. // Start from back because the most common case is closing the top-most modal window
  492. for (auto iter = mData->mModalWindowStack.rbegin(); iter != mData->mModalWindowStack.rend(); ++iter)
  493. {
  494. if (*iter == win)
  495. {
  496. auto iterFwd = std::next(iter).base(); // erase doesn't accept reverse iter, so convert
  497. mData->mModalWindowStack.erase(iterFwd);
  498. break;
  499. }
  500. }
  501. if (!mData->mModalWindowStack.empty()) // Enable next modal window
  502. {
  503. RenderWindowCore* curModalWindow = mData->mModalWindowStack.back();
  504. UINT64 curHwnd;
  505. curModalWindow->getCustomAttribute("WINDOW", &curHwnd);
  506. EnableWindow((HWND)curHwnd, TRUE);
  507. }
  508. else
  509. reenableWindows = true; // No more modal windows, re-enable any remaining window
  510. }
  511. if(reenableWindows)
  512. {
  513. Vector<RenderWindowCore*> renderWindows = RenderWindowCoreManager::instance().getRenderWindows();
  514. for(auto& renderWindow : renderWindows)
  515. {
  516. HWND curHwnd;
  517. renderWindow->getCustomAttribute("WINDOW", &curHwnd);
  518. EnableWindow(curHwnd, TRUE);
  519. }
  520. }
  521. return 0;
  522. }
  523. case WM_SETFOCUS:
  524. {
  525. if (!win->getProperties().hasFocus())
  526. win->_windowFocusReceived();
  527. return 0;
  528. }
  529. case WM_KILLFOCUS:
  530. {
  531. if (win->getProperties().hasFocus())
  532. win->_windowFocusLost();
  533. return 0;
  534. }
  535. case WM_SYSCHAR:
  536. // return zero to bypass defProc and signal we processed the message, unless it's an ALT-space
  537. if (wParam != VK_SPACE)
  538. return 0;
  539. break;
  540. case WM_MOVE:
  541. win->_windowMovedOrResized();
  542. return 0;
  543. case WM_DISPLAYCHANGE:
  544. win->_windowMovedOrResized();
  545. break;
  546. case WM_SIZE:
  547. win->_windowMovedOrResized();
  548. if (wParam == SIZE_MAXIMIZED)
  549. win->_notifyMaximized();
  550. else if (wParam == SIZE_MINIMIZED)
  551. win->_notifyMinimized();
  552. else if (wParam == SIZE_RESTORED)
  553. win->_notifyRestored();
  554. return 0;
  555. case WM_SETCURSOR:
  556. if(isCursorHidden())
  557. SetCursor(nullptr);
  558. else
  559. {
  560. switch (LOWORD(lParam))
  561. {
  562. case HTTOPLEFT:
  563. SetCursor(LoadCursor(0, IDC_SIZENWSE));
  564. return 0;
  565. case HTTOP:
  566. SetCursor(LoadCursor(0, IDC_SIZENS));
  567. return 0;
  568. case HTTOPRIGHT:
  569. SetCursor(LoadCursor(0, IDC_SIZENESW));
  570. return 0;
  571. case HTLEFT:
  572. SetCursor(LoadCursor(0, IDC_SIZEWE));
  573. return 0;
  574. case HTRIGHT:
  575. SetCursor(LoadCursor(0, IDC_SIZEWE));
  576. return 0;
  577. case HTBOTTOMLEFT:
  578. SetCursor(LoadCursor(0, IDC_SIZENESW));
  579. return 0;
  580. case HTBOTTOM:
  581. SetCursor(LoadCursor(0, IDC_SIZENS));
  582. return 0;
  583. case HTBOTTOMRIGHT:
  584. SetCursor(LoadCursor(0, IDC_SIZENWSE));
  585. return 0;
  586. }
  587. SetCursor(mData->mCursor.cursor);
  588. }
  589. return true;
  590. case WM_GETMINMAXINFO:
  591. {
  592. // Prevent the window from going smaller than some minimu size
  593. ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100;
  594. ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100;
  595. // Ensure maximizes window has proper size and doesn't cover the entire screen
  596. const POINT ptZero = { 0, 0 };
  597. HMONITOR primaryMonitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
  598. MONITORINFO monitorInfo;
  599. monitorInfo.cbSize = sizeof(MONITORINFO);
  600. GetMonitorInfo(primaryMonitor, &monitorInfo);
  601. ((MINMAXINFO*)lParam)->ptMaxPosition.x = monitorInfo.rcWork.left - monitorInfo.rcMonitor.left;
  602. ((MINMAXINFO*)lParam)->ptMaxPosition.y = monitorInfo.rcWork.top - monitorInfo.rcMonitor.top;
  603. ((MINMAXINFO*)lParam)->ptMaxSize.x = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
  604. ((MINMAXINFO*)lParam)->ptMaxSize.y = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
  605. }
  606. break;
  607. case WM_CLOSE:
  608. {
  609. gCoreApplication().quitRequested();
  610. return 0;
  611. }
  612. case WM_NCHITTEST:
  613. {
  614. auto iterFind = mData->mNonClientAreas.find(win);
  615. if (iterFind == mData->mNonClientAreas.end())
  616. break;
  617. POINT mousePos;
  618. mousePos.x = GET_X_LPARAM(lParam);
  619. mousePos.y = GET_Y_LPARAM(lParam);
  620. ScreenToClient(hWnd, &mousePos);
  621. Vector2I mousePosInt;
  622. mousePosInt.x = mousePos.x;
  623. mousePosInt.y = mousePos.y;
  624. Vector<NonClientResizeArea>& resizeAreasPerWindow = iterFind->second.resizeAreas;
  625. for(auto area : resizeAreasPerWindow)
  626. {
  627. if(area.area.contains(mousePosInt))
  628. return translateNonClientAreaType(area.type);
  629. }
  630. Vector<Rect2I>& moveAreasPerWindow = iterFind->second.moveAreas;
  631. for(auto area : moveAreasPerWindow)
  632. {
  633. if(area.contains(mousePosInt))
  634. return HTCAPTION;
  635. }
  636. return HTCLIENT;
  637. }
  638. case WM_NCLBUTTONDBLCLK:
  639. // Maximize/Restore on double-click
  640. if (wParam == HTCAPTION)
  641. {
  642. WINDOWPLACEMENT windowPlacement;
  643. windowPlacement.length = sizeof(WINDOWPLACEMENT);
  644. GetWindowPlacement(hWnd, &windowPlacement);
  645. if (windowPlacement.showCmd == SW_MAXIMIZE)
  646. ShowWindow(hWnd, SW_RESTORE);
  647. else
  648. ShowWindow(hWnd, SW_MAXIMIZE);
  649. return 0;
  650. }
  651. break;
  652. case WM_MOUSELEAVE:
  653. {
  654. // Note: Right now I track only mouse leaving client area. So it's possible for the "mouse left window" callback
  655. // to trigger, while the mouse is still in the non-client area of the window.
  656. mData->mIsTrackingMouse = false; // TrackMouseEvent ends when this message is received and needs to be re-applied
  657. BS_LOCK_MUTEX(mData->mSync);
  658. if (!onMouseLeftWindow.empty())
  659. onMouseLeftWindow(win);
  660. }
  661. return 0;
  662. case WM_LBUTTONUP:
  663. {
  664. ReleaseCapture();
  665. Vector2I intMousePos;
  666. OSPointerButtonStates btnStates;
  667. getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
  668. if(!onCursorButtonReleased.empty())
  669. onCursorButtonReleased(intMousePos, OSMouseButton::Left, btnStates);
  670. return 0;
  671. }
  672. case WM_MBUTTONUP:
  673. {
  674. ReleaseCapture();
  675. Vector2I intMousePos;
  676. OSPointerButtonStates btnStates;
  677. getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
  678. if(!onCursorButtonReleased.empty())
  679. onCursorButtonReleased(intMousePos, OSMouseButton::Middle, btnStates);
  680. return 0;
  681. }
  682. case WM_RBUTTONUP:
  683. {
  684. ReleaseCapture();
  685. Vector2I intMousePos;
  686. OSPointerButtonStates btnStates;
  687. getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
  688. if(!onCursorButtonReleased.empty())
  689. onCursorButtonReleased(intMousePos, OSMouseButton::Right, btnStates);
  690. return 0;
  691. }
  692. case WM_LBUTTONDOWN:
  693. {
  694. SetCapture(hWnd);
  695. Vector2I intMousePos;
  696. OSPointerButtonStates btnStates;
  697. getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
  698. if(!onCursorButtonPressed.empty())
  699. onCursorButtonPressed(intMousePos, OSMouseButton::Left, btnStates);
  700. }
  701. return 0;
  702. case WM_MBUTTONDOWN:
  703. {
  704. SetCapture(hWnd);
  705. Vector2I intMousePos;
  706. OSPointerButtonStates btnStates;
  707. getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
  708. if(!onCursorButtonPressed.empty())
  709. onCursorButtonPressed(intMousePos, OSMouseButton::Middle, btnStates);
  710. }
  711. return 0;
  712. case WM_RBUTTONDOWN:
  713. {
  714. SetCapture(hWnd);
  715. Vector2I intMousePos;
  716. OSPointerButtonStates btnStates;
  717. getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
  718. if(!onCursorButtonPressed.empty())
  719. onCursorButtonPressed(intMousePos, OSMouseButton::Right, btnStates);
  720. }
  721. return 0;
  722. case WM_LBUTTONDBLCLK:
  723. {
  724. Vector2I intMousePos;
  725. OSPointerButtonStates btnStates;
  726. getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
  727. if(!onCursorDoubleClick.empty())
  728. onCursorDoubleClick(intMousePos, btnStates);
  729. }
  730. return 0;
  731. case WM_NCMOUSEMOVE:
  732. case WM_MOUSEMOVE:
  733. {
  734. // Set up tracking so we get notified when mouse leaves the window
  735. if(!mData->mIsTrackingMouse)
  736. {
  737. TRACKMOUSEEVENT tme = { sizeof(tme) };
  738. tme.dwFlags = TME_LEAVE;
  739. tme.hwndTrack = hWnd;
  740. TrackMouseEvent(&tme);
  741. mData->mIsTrackingMouse = true;
  742. }
  743. Vector2I intMousePos;
  744. OSPointerButtonStates btnStates;
  745. getMouseData(hWnd, wParam, lParam, uMsg == WM_NCMOUSEMOVE, intMousePos, btnStates);
  746. if(!onCursorMoved.empty())
  747. onCursorMoved(intMousePos, btnStates);
  748. return 0;
  749. }
  750. case WM_MOUSEWHEEL:
  751. {
  752. INT16 wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
  753. float wheelDeltaFlt = wheelDelta / (float)WHEEL_DELTA;
  754. if(!onMouseWheelScrolled.empty())
  755. onMouseWheelScrolled(wheelDeltaFlt);
  756. return true;
  757. }
  758. case WM_SYSKEYDOWN:
  759. case WM_KEYDOWN:
  760. {
  761. if(wParam == VK_SHIFT)
  762. {
  763. isShiftPressed = true;
  764. break;
  765. }
  766. if(wParam == VK_CONTROL)
  767. {
  768. isCtrlPressed = true;
  769. break;
  770. }
  771. InputCommandType command = InputCommandType::Backspace;
  772. if(getCommand((unsigned int)wParam, command))
  773. {
  774. if(!onInputCommand.empty())
  775. onInputCommand(command);
  776. return 0;
  777. }
  778. break;
  779. }
  780. case WM_SYSKEYUP:
  781. case WM_KEYUP:
  782. {
  783. if(wParam == VK_SHIFT)
  784. {
  785. isShiftPressed = false;
  786. }
  787. if(wParam == VK_CONTROL)
  788. {
  789. isCtrlPressed = false;
  790. }
  791. return 0;
  792. }
  793. case WM_CHAR:
  794. {
  795. // TODO - Not handling IME input
  796. // Ignore rarely used special command characters, usually triggered by ctrl+key
  797. // combinations. (We want to keep ctrl+key free for shortcuts instead)
  798. if (wParam <= 23)
  799. break;
  800. switch (wParam)
  801. {
  802. case VK_ESCAPE:
  803. break;
  804. default: // displayable character
  805. {
  806. UINT8 scanCode = (lParam >> 16) & 0xFF;
  807. BYTE keyState[256];
  808. HKL layout = GetKeyboardLayout(0);
  809. if(GetKeyboardState(keyState) == 0)
  810. return 0;
  811. unsigned int vk = MapVirtualKeyEx(scanCode, MAPVK_VSC_TO_VK_EX, layout);
  812. if(vk == 0)
  813. return 0;
  814. InputCommandType command = InputCommandType::Backspace;
  815. if(getCommand(vk, command, true)) // We ignore character combinations that are special commands
  816. return 0;
  817. UINT32 finalChar = (UINT32)wParam;
  818. if(!onCharInput.empty())
  819. onCharInput(finalChar);
  820. return 0;
  821. }
  822. }
  823. break;
  824. }
  825. case WM_BS_SETCAPTURE:
  826. SetCapture(hWnd);
  827. break;
  828. case WM_BS_RELEASECAPTURE:
  829. ReleaseCapture();
  830. break;
  831. case WM_CAPTURECHANGED:
  832. if(!onMouseCaptureChanged.empty())
  833. onMouseCaptureChanged();
  834. return 0;
  835. }
  836. return DefWindowProc( hWnd, uMsg, wParam, lParam );
  837. }
  838. }