BsWin32Window.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Win32/BsWin32Window.h"
  4. #include "Win32/BsWin32PlatformUtility.h"
  5. namespace BansheeEngine
  6. {
  7. Vector<Win32Window*> Win32Window::sAllWindows;
  8. Vector<Win32Window*> Win32Window::sModalWindowStack;
  9. Mutex Win32Window::sWindowsMutex;
  10. struct Win32Window::Pimpl
  11. {
  12. HWND hWnd = nullptr;
  13. INT32 left = 0;
  14. INT32 top = 0;
  15. UINT32 width = 0;
  16. UINT32 height = 0;
  17. bool isExternal = false;
  18. bool isModal = false;
  19. DWORD style = 0;
  20. DWORD styleEx = 0;
  21. };
  22. Win32Window::Win32Window(const WINDOW_DESC& desc)
  23. {
  24. m = bs_new<Pimpl>();
  25. m->isModal = desc.modal;
  26. HMONITOR hMonitor = desc.monitor;
  27. if (!desc.external)
  28. {
  29. m->style = WS_CLIPCHILDREN;
  30. INT32 left = desc.left;
  31. INT32 top = desc.top;
  32. // If we didn't specified the adapter index, or if we didn't find it
  33. if (hMonitor == nullptr)
  34. {
  35. POINT windowAnchorPoint;
  36. // Fill in anchor point.
  37. windowAnchorPoint.x = left;
  38. windowAnchorPoint.y = top;
  39. // Get the nearest monitor to this window.
  40. hMonitor = MonitorFromPoint(windowAnchorPoint, MONITOR_DEFAULTTOPRIMARY);
  41. }
  42. // Get the target monitor info
  43. MONITORINFO monitorInfo;
  44. memset(&monitorInfo, 0, sizeof(MONITORINFO));
  45. monitorInfo.cbSize = sizeof(MONITORINFO);
  46. GetMonitorInfo(hMonitor, &monitorInfo);
  47. UINT32 width = desc.width;
  48. UINT32 height = desc.height;
  49. // No specified top left -> Center the window in the middle of the monitor
  50. if (left == -1 || top == -1)
  51. {
  52. int screenw = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
  53. int screenh = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
  54. // clamp window dimensions to screen size
  55. int outerw = (int(width) < screenw) ? int(width) : screenw;
  56. int outerh = (int(height) < screenh) ? int(height) : screenh;
  57. if (left == -1)
  58. left = monitorInfo.rcWork.left + (screenw - outerw) / 2;
  59. else if (hMonitor != nullptr)
  60. left += monitorInfo.rcWork.left;
  61. if (top == -1)
  62. top = monitorInfo.rcWork.top + (screenh - outerh) / 2;
  63. else if (hMonitor != nullptr)
  64. top += monitorInfo.rcWork.top;
  65. }
  66. else if (hMonitor != nullptr)
  67. {
  68. left += monitorInfo.rcWork.left;
  69. top += monitorInfo.rcWork.top;
  70. }
  71. if (!desc.fullscreen)
  72. {
  73. if (desc.parent)
  74. {
  75. if (desc.toolWindow)
  76. m->styleEx = WS_EX_TOOLWINDOW;
  77. else
  78. m->style |= WS_CHILD;
  79. }
  80. else
  81. {
  82. if (desc.toolWindow)
  83. m->styleEx = WS_EX_TOOLWINDOW;
  84. }
  85. if (!desc.parent || desc.toolWindow)
  86. {
  87. if (desc.border == WindowBorder::None)
  88. m->style |= WS_POPUP;
  89. else if (desc.border == WindowBorder::Fixed)
  90. m->style |= WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
  91. else
  92. m->style |= WS_OVERLAPPEDWINDOW;
  93. }
  94. if (!desc.outerDimensions)
  95. {
  96. // Calculate window dimensions required to get the requested client area
  97. RECT rect;
  98. SetRect(&rect, 0, 0, width, height);
  99. AdjustWindowRect(&rect, m->style, false);
  100. width = rect.right - rect.left;
  101. height = rect.bottom - rect.top;
  102. // Clamp width and height to the desktop dimensions
  103. int screenw = GetSystemMetrics(SM_CXSCREEN);
  104. int screenh = GetSystemMetrics(SM_CYSCREEN);
  105. if ((int)width > screenw)
  106. width = screenw;
  107. if ((int)height > screenh)
  108. height = screenh;
  109. if (left < 0)
  110. left = (screenw - width) / 2;
  111. if (top < 0)
  112. top = (screenh - height) / 2;
  113. }
  114. if (desc.backgroundPixels != nullptr)
  115. m->styleEx |= WS_EX_LAYERED;
  116. }
  117. else
  118. {
  119. m->style |= WS_POPUP;
  120. top = 0;
  121. left = 0;
  122. }
  123. UINT classStyle = 0;
  124. if (desc.enableDoubleClick)
  125. classStyle |= CS_DBLCLKS;
  126. // Register the window class
  127. WNDCLASS wc = { classStyle, desc.wndProc, 0, 0, desc.module,
  128. LoadIcon(nullptr, IDI_APPLICATION), LoadCursor(nullptr, IDC_ARROW),
  129. (HBRUSH)GetStockObject(BLACK_BRUSH), 0, "Win32Wnd" };
  130. RegisterClass(&wc);
  131. // Create main window
  132. m->hWnd = CreateWindowEx(m->styleEx, "Win32Wnd", desc.title.c_str(), m->style,
  133. left, top, width, height, desc.parent, nullptr, desc.module, desc.creationParams);
  134. m->isExternal = false;
  135. }
  136. else
  137. {
  138. m->hWnd = desc.external;
  139. m->isExternal = true;
  140. }
  141. RECT rect;
  142. GetWindowRect(m->hWnd, &rect);
  143. m->top = rect.top;
  144. m->left = rect.left;
  145. GetClientRect(m->hWnd, &rect);
  146. m->width = rect.right;
  147. m->height = rect.bottom;
  148. // Set background, if any
  149. if (desc.backgroundPixels != nullptr)
  150. {
  151. HBITMAP backgroundBitmap = Win32PlatformUtility::createBitmap(
  152. desc.backgroundPixels, desc.backgroundWidth, desc.backgroundHeight, true);
  153. HDC hdcScreen = GetDC(nullptr);
  154. HDC hdcMem = CreateCompatibleDC(hdcScreen);
  155. HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, backgroundBitmap);
  156. BLENDFUNCTION blend = { 0 };
  157. blend.BlendOp = AC_SRC_OVER;
  158. blend.SourceConstantAlpha = 255;
  159. blend.AlphaFormat = AC_SRC_ALPHA;
  160. POINT origin;
  161. origin.x = m->left;
  162. origin.y = m->top;
  163. SIZE size;
  164. size.cx = m->width;
  165. size.cy = m->height;
  166. POINT zero = { 0 };
  167. UpdateLayeredWindow(m->hWnd, hdcScreen, &origin, &size,
  168. hdcMem, &zero, RGB(0, 0, 0), &blend, desc.alphaBlending ? ULW_ALPHA : ULW_OPAQUE);
  169. SelectObject(hdcMem, hOldBitmap);
  170. DeleteDC(hdcMem);
  171. ReleaseDC(nullptr, hdcScreen);
  172. }
  173. // Handle modal windows
  174. bs_frame_mark();
  175. {
  176. FrameVector<HWND> windowsToDisable;
  177. FrameVector<HWND> windowsToBringToFront;
  178. {
  179. BS_LOCK_MUTEX(sWindowsMutex);
  180. if (m->isModal)
  181. {
  182. if (!sModalWindowStack.empty())
  183. {
  184. Win32Window* curModalWindow = sModalWindowStack.back();
  185. windowsToDisable.push_back(curModalWindow->m->hWnd);
  186. }
  187. else
  188. {
  189. for (auto& window : sAllWindows)
  190. windowsToDisable.push_back(window->m->hWnd);
  191. }
  192. sModalWindowStack.push_back(this);
  193. }
  194. else
  195. {
  196. // A non-modal window was opened while another modal one is open,
  197. // immediately deactivate it and make sure the modal windows stay on top.
  198. if (!sModalWindowStack.empty())
  199. {
  200. windowsToDisable.push_back(m->hWnd);
  201. for (auto window : sModalWindowStack)
  202. windowsToBringToFront.push_back(window->m->hWnd);
  203. }
  204. }
  205. sAllWindows.push_back(this);
  206. }
  207. for(auto& entry : windowsToDisable)
  208. EnableWindow(entry, FALSE);
  209. for (auto& entry : windowsToBringToFront)
  210. BringWindowToTop(entry);
  211. }
  212. bs_frame_clear();
  213. }
  214. Win32Window::~Win32Window()
  215. {
  216. if (m->hWnd && !m->isExternal)
  217. {
  218. // Handle modal windows
  219. bs_frame_mark();
  220. {
  221. FrameVector<HWND> windowsToEnable;
  222. {
  223. BS_LOCK_MUTEX(sWindowsMutex);
  224. // Hidden dependency: All windows must be re-enabled before a window is destroyed, otherwise the incorrect
  225. // window in the z order will be activated.
  226. bool reenableWindows = false;
  227. if (!sModalWindowStack.empty())
  228. {
  229. // Start from back because the most common case is closing the top-most modal window
  230. for (auto iter = sModalWindowStack.rbegin(); iter != sModalWindowStack.rend(); ++iter)
  231. {
  232. if (*iter == this)
  233. {
  234. auto iterFwd = std::next(iter).base(); // erase doesn't accept reverse iter, so convert
  235. sModalWindowStack.erase(iterFwd);
  236. break;
  237. }
  238. }
  239. if (!sModalWindowStack.empty()) // Enable next modal window
  240. {
  241. Win32Window* curModalWindow = sModalWindowStack.back();
  242. windowsToEnable.push_back(curModalWindow->m->hWnd);
  243. }
  244. else
  245. reenableWindows = true; // No more modal windows, re-enable any remaining window
  246. }
  247. if (reenableWindows)
  248. {
  249. for (auto& window : sAllWindows)
  250. windowsToEnable.push_back(window->m->hWnd);
  251. }
  252. }
  253. for(auto& entry : windowsToEnable)
  254. EnableWindow(entry, TRUE);
  255. }
  256. bs_frame_clear();
  257. DestroyWindow(m->hWnd);
  258. }
  259. {
  260. BS_LOCK_MUTEX(sWindowsMutex);
  261. auto iterFind = std::find(sAllWindows.begin(), sAllWindows.end(), this);
  262. sAllWindows.erase(iterFind);
  263. }
  264. bs_delete(m);
  265. }
  266. void Win32Window::move(INT32 left, INT32 top)
  267. {
  268. if (m->hWnd)
  269. {
  270. m->top = top;
  271. m->left = left;
  272. SetWindowPos(m->hWnd, HWND_TOP, left, top, m->width, m->height, SWP_NOSIZE);
  273. }
  274. }
  275. void Win32Window::resize(UINT32 width, UINT32 height)
  276. {
  277. if (m->hWnd)
  278. {
  279. RECT rc = { 0, 0, (LONG)width, (LONG)height };
  280. AdjustWindowRect(&rc, GetWindowLong(m->hWnd, GWL_STYLE), false);
  281. width = rc.right - rc.left;
  282. height = rc.bottom - rc.top;
  283. m->width = width;
  284. m->height = height;
  285. SetWindowPos(m->hWnd, HWND_TOP, m->left, m->top, width, height, SWP_NOMOVE);
  286. }
  287. }
  288. void Win32Window::setActive(bool state)
  289. {
  290. if (m->hWnd)
  291. {
  292. if (state)
  293. ShowWindow(m->hWnd, SW_RESTORE);
  294. else
  295. ShowWindow(m->hWnd, SW_SHOWMINNOACTIVE);
  296. }
  297. }
  298. void Win32Window::setHidden(bool hidden)
  299. {
  300. if (hidden)
  301. ShowWindow(m->hWnd, SW_HIDE);
  302. else
  303. ShowWindow(m->hWnd, SW_SHOW);
  304. }
  305. void Win32Window::minimize()
  306. {
  307. if (m->hWnd)
  308. ShowWindow(m->hWnd, SW_MINIMIZE);
  309. }
  310. void Win32Window::maximize()
  311. {
  312. if (m->hWnd)
  313. ShowWindow(m->hWnd, SW_MAXIMIZE);
  314. }
  315. void Win32Window::restore()
  316. {
  317. if (m->hWnd)
  318. ShowWindow(m->hWnd, SW_RESTORE);
  319. }
  320. void Win32Window::_windowMovedOrResized()
  321. {
  322. if (!m->hWnd || IsIconic(m->hWnd))
  323. return;
  324. RECT rc;
  325. GetWindowRect(m->hWnd, &rc);
  326. m->top = rc.top;
  327. m->left = rc.left;
  328. GetClientRect(m->hWnd, &rc);
  329. m->width = rc.right - rc.left;
  330. m->height = rc.bottom - rc.top;
  331. }
  332. Vector2I Win32Window::screenToWindowPos(const Vector2I& screenPos) const
  333. {
  334. POINT pos;
  335. pos.x = screenPos.x;
  336. pos.y = screenPos.y;
  337. ScreenToClient(m->hWnd, &pos);
  338. return Vector2I(pos.x, pos.y);
  339. }
  340. Vector2I Win32Window::windowToScreenPos(const Vector2I& windowPos) const
  341. {
  342. POINT pos;
  343. pos.x = windowPos.x;
  344. pos.y = windowPos.y;
  345. ClientToScreen(m->hWnd, &pos);
  346. return Vector2I(pos.x, pos.y);
  347. }
  348. INT32 Win32Window::getLeft() const
  349. {
  350. return m->left;
  351. }
  352. INT32 Win32Window::getTop() const
  353. {
  354. return m->top;
  355. }
  356. UINT32 Win32Window::getWidth() const
  357. {
  358. return m->width;
  359. }
  360. UINT32 Win32Window::getHeight() const
  361. {
  362. return m->height;
  363. }
  364. HWND Win32Window::getHWnd() const
  365. {
  366. return m->hWnd;
  367. }
  368. DWORD Win32Window::getStyle() const
  369. {
  370. return m->style;
  371. }
  372. DWORD Win32Window::getStyleEx() const
  373. {
  374. return m->styleEx;
  375. }
  376. void Win32Window::_enableAllWindows()
  377. {
  378. Vector<HWND> windowsToEnable;
  379. {
  380. BS_LOCK_MUTEX(sWindowsMutex);
  381. for (auto& window : sAllWindows)
  382. windowsToEnable.push_back(window->m->hWnd);
  383. }
  384. for (auto& entry : windowsToEnable)
  385. EnableWindow(entry, TRUE);
  386. }
  387. void Win32Window::_restoreModalWindows()
  388. {
  389. FrameVector<HWND> windowsToDisable;
  390. HWND bringToFrontHwnd = 0;
  391. {
  392. BS_LOCK_MUTEX(sWindowsMutex)
  393. if (!sModalWindowStack.empty())
  394. {
  395. Win32Window* curModalWindow = sModalWindowStack.back();
  396. bringToFrontHwnd = curModalWindow->m->hWnd;
  397. for (auto& window : sAllWindows)
  398. {
  399. if (window != curModalWindow)
  400. windowsToDisable.push_back(window->m->hWnd);
  401. }
  402. }
  403. }
  404. for (auto& entry : windowsToDisable)
  405. EnableWindow(entry, FALSE);
  406. if (bringToFrontHwnd != nullptr)
  407. BringWindowToTop(bringToFrontHwnd);
  408. }
  409. }