BsWin32Window.cpp 12 KB

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