BsWin32Platform.cpp 24 KB

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