BsWin32Window.cpp 11 KB

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