BsWin32Platform.cpp 26 KB

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