win32Window.cpp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #if !defined(TORQUE_SDL)
  23. #include <windows.h>
  24. #include <tchar.h>
  25. #include <winuser.h>
  26. #include "math/mMath.h"
  27. #include "gfx/gfxStructs.h"
  28. #include "windowManager/win32/win32Window.h"
  29. #include "windowManager/win32/win32WindowMgr.h"
  30. #include "windowManager/win32/win32CursorController.h"
  31. #include "windowManager/win32/winDispatch.h"
  32. #include "platform/menus/popupMenu.h"
  33. #include "platform/platformInput.h"
  34. // for winState structure
  35. #include "platformWin32/platformWin32.h"
  36. #include <d3d9types.h>
  37. #include "gfx/gfxDevice.h"
  38. #include <zmouse.h>
  39. const UTF16* _MainWindowClassName = L"TorqueJuggernaughtWindow";
  40. const UTF16* _CurtainWindowClassName = L"TorqueJuggernaughtCurtainWindow";
  41. #define SCREENSAVER_QUERY_DENY 0 // Disable screensaver
  42. #ifndef IDI_ICON1
  43. #define IDI_ICON1 107
  44. #endif
  45. static bool isScreenSaverRunning()
  46. {
  47. #ifndef SPI_GETSCREENSAVERRUNNING
  48. #define SPI_GETSCREENSAVERRUNNING 114
  49. #endif
  50. // Windows 2K, and higher. It might be better to hook into
  51. // the broadcast WM_SETTINGCHANGE message instead of polling for
  52. // the screen saver status.
  53. BOOL sreensaver = false;
  54. SystemParametersInfo(SPI_GETSCREENSAVERRUNNING,0,&sreensaver,0);
  55. return sreensaver;
  56. }
  57. DISPLAY_DEVICE GetPrimaryDevice()
  58. {
  59. int index = 0;
  60. DISPLAY_DEVICE dd;
  61. dd.cb = sizeof(DISPLAY_DEVICE);
  62. while (EnumDisplayDevices(NULL, index++, &dd, 0))
  63. {
  64. if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) return dd;
  65. }
  66. return dd;
  67. }
  68. Win32Window::Win32Window(): mMouseLockPosition(0,0),
  69. mShouldLockMouse(false),
  70. mMouseLocked(false),
  71. mOwningManager(NULL),
  72. mNextWindow(NULL),
  73. mWindowHandle(NULL),
  74. mOldParent(NULL),
  75. mTarget(NULL),
  76. mDevice(NULL),
  77. mAccelHandle(NULL),
  78. mSuppressReset(false),
  79. mMenuHandle(NULL),
  80. mWindowedWindowStyle(0),
  81. mPosition(0,0),
  82. mFullscreen(false)
  83. {
  84. mCursorController = new Win32CursorController( this );
  85. mVideoMode.bitDepth = 32;
  86. mVideoMode.fullScreen = false;
  87. mVideoMode.refreshRate = 60;
  88. mVideoMode.resolution.set(800,600);
  89. _registerWindowClass();
  90. }
  91. Win32Window::~Win32Window()
  92. {
  93. if(mAccelHandle)
  94. {
  95. DestroyAcceleratorTable(mAccelHandle);
  96. mAccelHandle = NULL;
  97. }
  98. // delete our win handle..
  99. DestroyWindow(mWindowHandle);
  100. // unlink ourselves from the window list...
  101. AssertFatal(mOwningManager, "Win32Window::~Win32Window - orphan window, cannot unlink!");
  102. mOwningManager->unlinkWindow(this);
  103. _unregisterWindowClass();
  104. }
  105. void* Win32Window::getSystemWindow(const WindowSystem system)
  106. {
  107. if( system == WindowSystem_Windows)
  108. return getHWND();
  109. return NULL;
  110. }
  111. GFXDevice * Win32Window::getGFXDevice()
  112. {
  113. return mDevice;
  114. }
  115. GFXWindowTarget * Win32Window::getGFXTarget()
  116. {
  117. return mTarget;
  118. }
  119. const GFXVideoMode & Win32Window::getVideoMode()
  120. {
  121. return mVideoMode;
  122. }
  123. void Win32Window::setVideoMode( const GFXVideoMode &mode )
  124. {
  125. bool needCurtain = (mVideoMode.fullScreen != mode.fullScreen);
  126. if(needCurtain)
  127. {
  128. Con::errorf("Win32Window::setVideoMode - invoking curtain");
  129. mOwningManager->lowerCurtain();
  130. }
  131. mVideoMode = mode;
  132. mSuppressReset = true;
  133. // Can't switch to fullscreen while a child of another window
  134. if(mode.fullScreen && !Platform::getWebDeployment() && mOwningManager->getParentWindow())
  135. {
  136. mOldParent = (HWND)mOwningManager->getParentWindow();
  137. mOwningManager->setParentWindow(NULL);
  138. }
  139. else if(!mode.fullScreen && mOldParent)
  140. {
  141. mOwningManager->setParentWindow(mOldParent);
  142. mOldParent = NULL;
  143. }
  144. // Set our window to have the right style based on the mode
  145. if(mode.fullScreen && !Platform::getWebDeployment() && !mOffscreenRender)
  146. {
  147. WINDOWPLACEMENT wplacement = { sizeof(wplacement) };
  148. DWORD dwStyle = GetWindowLong(getHWND(), GWL_STYLE);
  149. MONITORINFO mi = { sizeof(mi) };
  150. if (GetWindowPlacement(getHWND(), &wplacement) && GetMonitorInfo(MonitorFromWindow(getHWND(), MONITOR_DEFAULTTOPRIMARY), &mi))
  151. {
  152. DISPLAY_DEVICE dd = GetPrimaryDevice();
  153. DEVMODE dv;
  154. ZeroMemory(&dv, sizeof(dv));
  155. dv.dmSize = sizeof(DEVMODE);
  156. EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dv);
  157. dv.dmPelsWidth = mode.resolution.x;
  158. dv.dmPelsHeight = mode.resolution.y;
  159. dv.dmBitsPerPel = mode.bitDepth;
  160. dv.dmDisplayFrequency = mode.refreshRate;
  161. dv.dmFields = (DM_PELSWIDTH | DM_PELSHEIGHT);
  162. ChangeDisplaySettings(&dv, CDS_FULLSCREEN);
  163. SetWindowLong(getHWND(), GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW);
  164. SetWindowPos(getHWND(), HWND_TOP,
  165. mi.rcMonitor.left,
  166. mi.rcMonitor.top,
  167. mi.rcMonitor.right - mi.rcMonitor.left,
  168. mi.rcMonitor.bottom - mi.rcMonitor.top,
  169. SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
  170. }
  171. if(mDisplayWindow)
  172. ShowWindow(getHWND(), SW_SHOWNORMAL);
  173. // Clear the menu bar from the window for full screen
  174. HMENU menu = GetMenu(getHWND());
  175. if(menu)
  176. {
  177. SetMenu(getHWND(), NULL);
  178. }
  179. // When switching to Fullscreen, reset device after setting style
  180. if(mTarget.isValid())
  181. mTarget->resetMode();
  182. mFullscreen = true;
  183. }
  184. else
  185. {
  186. DISPLAY_DEVICE dd = GetPrimaryDevice();
  187. DEVMODE dv;
  188. ZeroMemory(&dv, sizeof(dv));
  189. dv.dmSize = sizeof(DEVMODE);
  190. EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dv);
  191. if ((mode.resolution.x != dv.dmPelsWidth) || (mode.resolution.y != dv.dmPelsHeight))
  192. ChangeDisplaySettings(NULL, 0);
  193. // Reset device *first*, so that when we call setSize() and let it
  194. // access the monitor settings, it won't end up with our fullscreen
  195. // geometry that is just about to change.
  196. if(mTarget.isValid())
  197. mTarget->resetMode();
  198. if (!mOffscreenRender)
  199. {
  200. SetWindowLong( getHWND(), GWL_STYLE, mWindowedWindowStyle);
  201. SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
  202. // Put back the menu bar, if any
  203. if(mMenuHandle)
  204. {
  205. SetMenu(getHWND(), mMenuHandle);
  206. }
  207. }
  208. // Make sure we're the correct resolution for web deployment
  209. if (!Platform::getWebDeployment() || !mOwningManager->getParentWindow() || mOffscreenRender)
  210. {
  211. setSize(mode.resolution);
  212. }
  213. else
  214. {
  215. HWND parentWin = (HWND)mOwningManager->getParentWindow();
  216. RECT windowRect;
  217. GetClientRect(parentWin, &windowRect);
  218. Point2I res(windowRect.right-windowRect.left, windowRect.bottom-windowRect.top);
  219. if (res.x == 0 || res.y == 0)
  220. {
  221. // Must be too early in the window set up to obtain the parent's size.
  222. setSize(mode.resolution);
  223. }
  224. else
  225. {
  226. setSize(res);
  227. }
  228. }
  229. if (!mOffscreenRender)
  230. {
  231. // We have to force Win32 to update the window frame and make the window
  232. // visible and no longer topmost - this code might be possible to simplify.
  233. SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
  234. if(mDisplayWindow)
  235. ShowWindow( getHWND(), SW_SHOWNORMAL);
  236. }
  237. mFullscreen = false;
  238. }
  239. mSuppressReset = false;
  240. if(needCurtain)
  241. mOwningManager->raiseCurtain();
  242. SetForegroundWindow(getHWND());
  243. getScreenResChangeSignal().trigger(this, true);
  244. }
  245. bool Win32Window::clearFullscreen()
  246. {
  247. return true;
  248. }
  249. bool Win32Window::isFullscreen()
  250. {
  251. return mFullscreen;
  252. }
  253. void Win32Window::_setFullscreen(const bool fullscreen)
  254. {
  255. if (fullscreen == mFullscreen)
  256. return;
  257. mFullscreen = fullscreen;
  258. if(fullscreen && !mOffscreenRender)
  259. {
  260. Con::printf("Win32Window::setFullscreen (full) enter");
  261. SetWindowLong( getHWND(), GWL_STYLE, WS_POPUP|WS_SYSMENU );
  262. SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
  263. }
  264. else
  265. {
  266. Con::printf("Win32Window::setFullscreen (windowed) enter");
  267. if (!mOffscreenRender)
  268. {
  269. SetWindowLong( getHWND(), GWL_STYLE, mWindowedWindowStyle);
  270. SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, mVideoMode.resolution.x, mVideoMode.resolution.y, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
  271. }
  272. setSize(mVideoMode.resolution);
  273. }
  274. Con::printf("Win32Window::setFullscreen exit");
  275. }
  276. bool Win32Window::setCaption( const char *cap )
  277. {
  278. return SetWindowTextA(mWindowHandle, cap);
  279. }
  280. const char * Win32Window::getCaption()
  281. {
  282. char buff[512];
  283. S32 strLen = GetWindowTextA(mWindowHandle, buff, 512);
  284. if(strLen==0)
  285. return NULL;
  286. return StringTable->insert(buff);
  287. }
  288. void Win32Window::setFocus()
  289. {
  290. ::SetFocus( mWindowHandle );
  291. }
  292. void Win32Window::setClientExtent( const Point2I newExtent )
  293. {
  294. Point2I oldExtent = getClientExtent();
  295. if (oldExtent == newExtent)
  296. return;
  297. RECT rtClient;
  298. DWORD Style, ExStyle;
  299. SetRect( &rtClient, 0, 0, newExtent.x, newExtent.y );
  300. Style = GetWindowLong( mWindowHandle, GWL_STYLE);
  301. ExStyle = GetWindowLong( mWindowHandle, GWL_EXSTYLE );
  302. AdjustWindowRectEx( &rtClient, Style, getMenuHandle() != NULL, ExStyle );
  303. if( Style & WS_VSCROLL )
  304. rtClient.right += GetSystemMetrics( SM_CXVSCROLL );
  305. if( Style & WS_HSCROLL )
  306. rtClient.bottom += GetSystemMetrics( SM_CYVSCROLL );
  307. SetWindowPos( mWindowHandle, NULL, 0, 0, rtClient.right - rtClient.left, rtClient.bottom - rtClient.top, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
  308. }
  309. const Point2I Win32Window::getClientExtent()
  310. {
  311. // Fetch Client Rect from Windows
  312. RECT clientRect;
  313. ::GetClientRect(mWindowHandle, &clientRect);
  314. // Return as a Torque Point2I - We don't care about origin as it's always 0,0
  315. return Point2I(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
  316. }
  317. void Win32Window::setBounds( const RectI &newBounds )
  318. {
  319. RECT newRect;
  320. newRect.left = newBounds.point.x;
  321. newRect.top = newBounds.point.y;
  322. newRect.bottom = newRect.top + newBounds.extent.y;
  323. newRect.right = newRect.left + newBounds.extent.x;
  324. MoveWindow(mWindowHandle, newRect.left, newRect.top, newRect.right - newRect.left, newRect.bottom - newRect.top, true);
  325. }
  326. const RectI Win32Window::getBounds() const
  327. {
  328. // Fetch Window Rect from OS
  329. RECT windowRect;
  330. ::GetWindowRect(mWindowHandle, &windowRect);
  331. // Return as a Torque RectI
  332. return RectI(windowRect.left,windowRect.top,windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
  333. }
  334. void Win32Window::setPosition( const Point2I newPosition )
  335. {
  336. SetWindowPos( mWindowHandle, HWND_NOTOPMOST, newPosition.x, newPosition.y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE );
  337. }
  338. const Point2I Win32Window::getPosition()
  339. {
  340. RECT windowRect;
  341. GetWindowRect( mWindowHandle, &windowRect );
  342. // Return position
  343. return Point2I(windowRect.left,windowRect.top);
  344. }
  345. Point2I Win32Window::clientToScreen( const Point2I& pos )
  346. {
  347. POINT p = { pos.x, pos.y };
  348. ClientToScreen( mWindowHandle, &p );
  349. return Point2I( p.x, p.y );
  350. }
  351. Point2I Win32Window::screenToClient( const Point2I& pos )
  352. {
  353. POINT p = { pos.x, pos.y };
  354. ScreenToClient( mWindowHandle, &p );
  355. return Point2I( p.x, p.y );
  356. }
  357. void Win32Window::centerWindow()
  358. {
  359. RECT newRect;
  360. GetWindowRect(mWindowHandle,&newRect);
  361. newRect.bottom -= newRect.top;
  362. newRect.right -= newRect.left;
  363. newRect.top = 0;
  364. newRect.left = 0;
  365. HMONITOR hMon = MonitorFromWindow(mWindowHandle, MONITOR_DEFAULTTONEAREST);
  366. // Get the monitor's extents.
  367. MONITORINFO monInfo;
  368. dMemset(&monInfo, 0, sizeof(MONITORINFO));
  369. monInfo.cbSize = sizeof(MONITORINFO);
  370. GetMonitorInfo(hMon, &monInfo);
  371. // Calculate the offset to center the window in the working area
  372. S32 deltaX = ((monInfo.rcWork.right - monInfo.rcWork.left) / 2) - ((newRect.right - newRect.left) / 2);
  373. S32 deltaY = ((monInfo.rcWork.bottom - monInfo.rcWork.top) / 2) - ((newRect.bottom - newRect.top) / 2);
  374. // Calculate the new left and top position for the window
  375. S32 newLeft = newRect.left + deltaX;
  376. S32 newTop = newRect.top + deltaY;
  377. // Clamp these to be greater than 0 so that the top left corner is never offscreen
  378. newLeft = mClamp(newLeft, 0, newLeft);
  379. newTop = mClamp(newLeft, 0, newTop);
  380. // Calculate the new width and height
  381. S32 newWidth = newRect.right - newRect.left;
  382. S32 newHeight = newRect.bottom - newRect.top;
  383. // If the new width and height of the window is larger
  384. // than the working area of the monitor but is smaller
  385. // than the monitor size then have it max out at the
  386. // working area so that it will remain uncovered. We
  387. // leave it alone if it is bigger than the monitor size
  388. // (with a small fudge) to support multiple monitors.
  389. if (newLeft + newWidth > (monInfo.rcWork.right - monInfo.rcWork.left) &&
  390. newLeft + newWidth <= (monInfo.rcMonitor.right - monInfo.rcMonitor.left) + 4)
  391. newWidth = (monInfo.rcWork.right - monInfo.rcWork.left) - newLeft;
  392. if (newTop + newHeight > (monInfo.rcWork.bottom - monInfo.rcWork.top) &&
  393. newTop + newHeight <= (monInfo.rcMonitor.bottom - monInfo.rcMonitor.top) + 4)
  394. newHeight = (monInfo.rcWork.bottom - monInfo.rcWork.top) - newTop;
  395. MoveWindow( mWindowHandle, newLeft, newTop, newWidth, newHeight, true );
  396. // Make sure the resolution matches the client extent
  397. Point2I clientExt = getClientExtent();
  398. mVideoMode.resolution.set( clientExt.x, clientExt.y );
  399. // Let GFX get an update about the new resolution
  400. if (mTarget.isValid())
  401. mTarget->resetMode();
  402. }
  403. bool Win32Window::setSize( const Point2I &newSize )
  404. {
  405. // Create the window rect (screen centered if not owned by a parent)
  406. RECT newRect;
  407. newRect.left = 0;
  408. newRect.top = 0;
  409. newRect.bottom = newRect.top + newSize.y;
  410. newRect.right = newRect.left + newSize.x;
  411. // Adjust the window rect to ensure the client rectangle is the desired resolution
  412. AdjustWindowRect( &newRect, mWindowedWindowStyle, false);//(bool)(getMenuHandle() != NULL) );
  413. // Center the window on the screen if we're not a child
  414. if( !mOwningManager->mParentWindow )
  415. {
  416. HMONITOR hMon = MonitorFromWindow(mWindowHandle, MONITOR_DEFAULTTONEAREST);
  417. // Get the monitor's extents.
  418. MONITORINFO monInfo;
  419. dMemset(&monInfo, 0, sizeof(MONITORINFO));
  420. monInfo.cbSize = sizeof(MONITORINFO);
  421. GetMonitorInfo(hMon, &monInfo);
  422. // Calculate the offset to center the window in the working area
  423. S32 deltaX = ((monInfo.rcWork.right - monInfo.rcWork.left) / 2) - ((newRect.right - newRect.left) / 2);
  424. S32 deltaY = ((monInfo.rcWork.bottom - monInfo.rcWork.top) / 2) - ((newRect.bottom - newRect.top) / 2);
  425. // Calculate the new left and top position for the window
  426. S32 newLeft = newRect.left + deltaX;
  427. S32 newTop = newRect.top + deltaY;
  428. // Clamp these to be greater than 0 so that the top left corner is never offscreen
  429. newLeft = mClamp(newLeft, 0, newLeft);
  430. newTop = mClamp(newLeft, 0, newTop);
  431. // Calculate the new width and height
  432. S32 newWidth = newRect.right - newRect.left;
  433. S32 newHeight = newRect.bottom - newRect.top;
  434. // If the new width and height of the window is larger
  435. // than the working area of the monitor but is smaller
  436. // than the monitor size then have it max out at the
  437. // working area so that it will remain uncovered. We
  438. // leave it alone if it is bigger than the monitor size
  439. // (with a small fudge) to support multiple monitors.
  440. if (newLeft + newWidth > (monInfo.rcWork.right - monInfo.rcWork.left) &&
  441. newLeft + newWidth <= (monInfo.rcMonitor.right - monInfo.rcMonitor.left) + 4)
  442. newWidth = (monInfo.rcWork.right - monInfo.rcWork.left) - newLeft;
  443. if (newTop + newHeight > (monInfo.rcWork.bottom - monInfo.rcWork.top) &&
  444. newTop + newHeight <= (monInfo.rcMonitor.bottom - monInfo.rcMonitor.top) + 4)
  445. newHeight = (monInfo.rcWork.bottom - monInfo.rcWork.top) - newTop;
  446. MoveWindow( mWindowHandle, newLeft, newTop, newWidth, newHeight, true );
  447. }
  448. else // Just position it according to the mPosition plus new extent
  449. MoveWindow(mWindowHandle, newRect.left, newRect.top, newRect.right - newRect.left, newRect.bottom - newRect.top, true);
  450. // Make sure the resolution matches the client extent
  451. Point2I clientExt = getClientExtent();
  452. mVideoMode.resolution.set( clientExt.x, clientExt.y );
  453. // Let GFX get an update about the new resolution
  454. if (mTarget.isValid())
  455. mTarget->resetMode();
  456. InvalidateRect( NULL, NULL, true );
  457. return true;
  458. }
  459. bool Win32Window::isOpen()
  460. {
  461. return true;
  462. }
  463. bool Win32Window::isVisible()
  464. {
  465. // Is the window open and visible, ie. not minimized?
  466. if(!mWindowHandle)
  467. return false;
  468. if (mOffscreenRender)
  469. return true;
  470. return IsWindowVisible(mWindowHandle)
  471. && !IsIconic(mWindowHandle)
  472. && !isScreenSaverRunning();
  473. }
  474. bool Win32Window::isFocused()
  475. {
  476. if (mOffscreenRender)
  477. return true;
  478. // CodeReview This is enough to make the plugin and normal/editor scenarios
  479. // coexist but it seems brittle. I think we need a better way to detect
  480. // if we're the foreground window, maybe taking into account if any of our
  481. // window's parents are foreground? [bjg 4/30/07]
  482. if(mOwningManager->mParentWindow)
  483. return (GetFocus() == mWindowHandle || IsChild(mWindowHandle, GetFocus()));
  484. else
  485. return ((GetFocus() == mWindowHandle || IsChild(mWindowHandle, GetFocus())) && GetForegroundWindow() == mWindowHandle);
  486. }
  487. bool Win32Window::isMinimized()
  488. {
  489. if (mOffscreenRender)
  490. return false;
  491. WINDOWPLACEMENT wd;
  492. if ( GetWindowPlacement( mWindowHandle, &wd ) )
  493. {
  494. return ( wd.showCmd == SW_SHOWMINIMIZED );
  495. }
  496. return false;
  497. }
  498. bool Win32Window::isMaximized()
  499. {
  500. if (mOffscreenRender)
  501. return true;
  502. WINDOWPLACEMENT wd;
  503. if ( GetWindowPlacement( mWindowHandle, &wd ) )
  504. {
  505. return ( wd.showCmd == SW_SHOWMAXIMIZED );
  506. }
  507. return false;
  508. }
  509. WindowId Win32Window::getWindowId()
  510. {
  511. return mWindowId;
  512. }
  513. void Win32Window::minimize()
  514. {
  515. if (mOffscreenRender)
  516. return;
  517. ShowWindow( mWindowHandle, SW_MINIMIZE );
  518. }
  519. void Win32Window::maximize()
  520. {
  521. if (mOffscreenRender)
  522. return;
  523. ShowWindow( mWindowHandle, SW_MAXIMIZE );
  524. }
  525. void Win32Window::restore()
  526. {
  527. if (mOffscreenRender)
  528. return;
  529. ShowWindow( mWindowHandle, SW_RESTORE );
  530. }
  531. void Win32Window::hide()
  532. {
  533. if (mOffscreenRender)
  534. return;
  535. ShowWindow( mWindowHandle, SW_HIDE );
  536. }
  537. void Win32Window::show()
  538. {
  539. if (mOffscreenRender)
  540. return;
  541. ShowWindow( mWindowHandle, SW_SHOWNORMAL );
  542. }
  543. void Win32Window::close()
  544. {
  545. delete this;
  546. }
  547. void Win32Window::_registerWindowClass()
  548. {
  549. // Check to see if it exists already.
  550. WNDCLASSEX classInfo;
  551. if (GetClassInfoEx(GetModuleHandle(NULL),_MainWindowClassName,&classInfo))
  552. return;
  553. HMODULE appInstance = GetModuleHandle(NULL);
  554. HICON appIcon = LoadIcon(appInstance, MAKEINTRESOURCE(IDI_ICON1));
  555. // Window class shared by all MainWindow objects
  556. classInfo.lpszClassName = _MainWindowClassName;
  557. classInfo.cbSize = sizeof(WNDCLASSEX);
  558. classInfo.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  559. classInfo.lpfnWndProc = (WNDPROC)WindowProc;
  560. classInfo.hInstance = appInstance; // Owner of this class
  561. classInfo.hIcon = appIcon; // Icon name
  562. classInfo.hIconSm = appIcon; // Icon name
  563. classInfo.hCursor = LoadCursor(NULL, IDC_ARROW); // Cursor
  564. classInfo.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // Default color
  565. classInfo.lpszMenuName = NULL;
  566. classInfo.cbClsExtra = 0;
  567. classInfo.cbWndExtra = 0;
  568. if (!RegisterClassEx(&classInfo))
  569. AssertISV(false,"Window class initialization failed");
  570. classInfo.lpfnWndProc = DefWindowProc;
  571. classInfo.hCursor = NULL;
  572. classInfo.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
  573. classInfo.lpszClassName = _CurtainWindowClassName;
  574. if (!RegisterClassEx(&classInfo))
  575. AssertISV(false,"Curtain window class initialization failed");
  576. }
  577. void Win32Window::_unregisterWindowClass()
  578. {
  579. WNDCLASSEX classInfo;
  580. if (GetClassInfoEx(GetModuleHandle(NULL),_MainWindowClassName,&classInfo))
  581. UnregisterClass(_MainWindowClassName,GetModuleHandle(NULL));
  582. if (GetClassInfoEx(GetModuleHandle(NULL),_CurtainWindowClassName,&classInfo))
  583. UnregisterClass(_CurtainWindowClassName,GetModuleHandle(NULL));
  584. }
  585. LRESULT PASCAL Win32Window::WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
  586. {
  587. // CodeReview [tom, 4/30/2007] The two casts here seem somewhat silly and redundant ?
  588. Win32Window* window = (Win32Window*)((PlatformWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA));
  589. const WindowId devId = window ? window->getWindowId() : 0;
  590. if (window && window->getOffscreenRender())
  591. return DefWindowProc(hWnd, message, wParam, lParam);
  592. switch (message)
  593. {
  594. case WM_DISPLAYCHANGE:
  595. // Update the monitor list
  596. PlatformWindowManager::get()->buildMonitorsList();
  597. if(window && window->isVisible() && !window->mSuppressReset && window->getVideoMode().bitDepth != wParam)
  598. {
  599. Con::warnf("Win32Window::WindowProc - resetting device due to display mode BPP change.");
  600. window->getGFXTarget()->resetMode();
  601. }
  602. break;
  603. case WM_MOUSEACTIVATE:
  604. SetFocus(hWnd);
  605. return MA_ACTIVATE;
  606. case WM_MOUSEMOVE:
  607. if (window && GetFocus() != hWnd && IsChild(hWnd, GetFocus()))
  608. {
  609. SetFocus(hWnd);
  610. break;
  611. }
  612. // If our foreground window is the browser and we don't have focus grab it
  613. if (Platform::getWebDeployment() && GetFocus() != hWnd)
  614. {
  615. HWND phwnd = GetParent(hWnd);
  616. while (phwnd)
  617. {
  618. if (GetForegroundWindow() == phwnd)
  619. {
  620. SetFocus(hWnd);
  621. break;
  622. }
  623. phwnd = GetParent(phwnd);
  624. }
  625. }
  626. break;
  627. // Associate the window pointer with this window
  628. case WM_CREATE:
  629. // CodeReview [tom, 4/30/2007] Why don't we just cast this to a LONG
  630. // instead of having a ton of essentially pointless casts ?
  631. SetWindowLongPtr(hWnd, GWLP_USERDATA,
  632. (LONG_PTR)((PlatformWindow*)((CREATESTRUCT*)lParam)->lpCreateParams));
  633. break;
  634. case WM_SETFOCUS:
  635. // NOTE: if wParam is NOT equal to our window handle then we are GAINING focus
  636. Dispatch(DelayedDispatch, hWnd, message, wParam, lParam);
  637. return 0;
  638. case WM_KILLFOCUS:
  639. // NOTE: if wParam is NOT equal to our window handle then we are LOSING focus
  640. Dispatch(DelayedDispatch, hWnd, message, wParam, lParam);
  641. return 0;
  642. // The window is being dragged
  643. case WM_MOVE:
  644. if(!window)
  645. break;
  646. window->mPosition.x = (int)LOWORD(lParam);
  647. window->mPosition.y = (int)HIWORD(lParam);
  648. return 0;
  649. // Update viewport when the window moves
  650. case WM_SIZE:
  651. if(window && window->mSuppressReset)
  652. break;
  653. // This is dispatched immediately to prevent a race condition with journaling and window minimizing
  654. if (wParam != SIZE_MINIMIZED && !Journal::IsPlaying())
  655. Dispatch( ImmediateDispatch, hWnd,message,wParam,lParam );
  656. if(wParam != SIZE_MINIMIZED && window != NULL )
  657. {
  658. if(!window->mVideoMode.fullScreen)
  659. {
  660. U32 width = LOWORD( lParam );
  661. U32 height = HIWORD( lParam );
  662. window->mVideoMode.resolution.set( width, height );
  663. }
  664. if(window->getGFXTarget())
  665. {
  666. Con::warnf("Win32Window::WindowProc - resetting device due to window size change.");
  667. window->getGFXTarget()->resetMode();
  668. }
  669. }
  670. return 0;
  671. // Limit resize to a safe minimum
  672. case WM_GETMINMAXINFO:
  673. MINMAXINFO *winfo;
  674. winfo = (MINMAXINFO*)(lParam);
  675. if(window && window->mMinimumSize.lenSquared() > 0)
  676. {
  677. winfo->ptMinTrackSize.x = window->mMinimumSize.x;
  678. winfo->ptMinTrackSize.y = window->mMinimumSize.y;
  679. }
  680. //Is the window size locked?
  681. if (window && window->isSizeLocked())
  682. {
  683. Point2I lockedSize = window->getLockedSize();
  684. winfo->ptMinTrackSize.x = lockedSize.x;
  685. winfo->ptMinTrackSize.y = lockedSize.y;
  686. winfo->ptMaxTrackSize.x = lockedSize.x;
  687. winfo->ptMaxTrackSize.y = lockedSize.y;
  688. }
  689. break;
  690. // Override background erase so window doesn't get cleared
  691. case WM_ERASEBKGND:
  692. return 1;
  693. case WM_MENUSELECT:
  694. winState.renderThreadBlocked = true;
  695. break;
  696. // Refresh the window
  697. case WM_PAINT:
  698. // Use validate instead of begin/end paint, which seem to installs
  699. // some Dx clipping state that isn't getting restored properly
  700. ValidateRect(hWnd,0);
  701. // Skip it if we're dispatching.
  702. if(Journal::IsDispatching())
  703. break;
  704. if( window == NULL )
  705. break;
  706. //// Default render if..
  707. //// 1. We have no device
  708. //// 2. We have a device but it's not allowing rendering
  709. if( !window->getGFXDevice() || !window->getGFXDevice()->allowRender() )
  710. window->defaultRender();
  711. if( winState.renderThreadBlocked )
  712. window->displayEvent.trigger(devId);
  713. break;
  714. // Power shutdown query
  715. case WM_POWERBROADCAST: {
  716. if (wParam == PBT_APMQUERYSUSPEND)
  717. if (GetForegroundWindow() == hWnd)
  718. return BROADCAST_QUERY_DENY;
  719. break;
  720. }
  721. // Screensaver activation and monitor power requests
  722. case WM_SYSCOMMAND:
  723. switch (wParam) {
  724. case SC_SCREENSAVE:
  725. case SC_MONITORPOWER:
  726. if (GetForegroundWindow() == hWnd)
  727. return SCREENSAVER_QUERY_DENY;
  728. break;
  729. }
  730. break;
  731. // Menus
  732. case WM_COMMAND:
  733. {
  734. winState.renderThreadBlocked = false;
  735. if( window == NULL )
  736. break;
  737. // [tom, 8/21/2006] Pass off to the relevant PopupMenu if it's a menu
  738. // or accelerator command. PopupMenu will in turn hand off to script.
  739. //
  740. // Note: PopupMenu::handleSelect() will not do anything if the menu
  741. // item is disabled, so we don't need to deal with that here.
  742. S32 numItems = GetMenuItemCount(window->getMenuHandle());
  743. for(S32 i = 0;i < numItems;i++)
  744. {
  745. MENUITEMINFOA mi;
  746. mi.cbSize = sizeof(mi);
  747. mi.fMask = MIIM_DATA;
  748. if(GetMenuItemInfoA(window->getMenuHandle(), i, TRUE, &mi))
  749. {
  750. if(mi.fMask & MIIM_DATA && mi.dwItemData != 0)
  751. {
  752. PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
  753. PopupMenu::smSelectionEventHandled = false;
  754. PopupMenu::smPopupMenuEvent.trigger(mnu->getPopupGUID(), LOWORD(wParam));
  755. if (PopupMenu::smSelectionEventHandled)
  756. return 0;
  757. }
  758. }
  759. }
  760. }
  761. break;
  762. case WM_INITMENUPOPUP:
  763. {
  764. HMENU menu = (HMENU)wParam;
  765. MENUINFO mi;
  766. mi.cbSize = sizeof(mi);
  767. mi.fMask = MIM_MENUDATA;
  768. if(GetMenuInfo(menu, &mi) && mi.dwMenuData != 0)
  769. {
  770. PopupMenu *pm = (PopupMenu *)mi.dwMenuData;
  771. if(pm != NULL)
  772. pm->onMenuSelect();
  773. }
  774. }
  775. break;
  776. // Some events need to be consumed as well as queued up
  777. // for later dispatch.
  778. case WM_CLOSE:
  779. case WM_MOUSEWHEEL:
  780. #ifdef WM_MOUSEHWHEEL // Vista
  781. case WM_MOUSEHWHEEL:
  782. #endif
  783. // CodeReview This fixes some issues with inappropriate event handling
  784. // around device resets and in full-screen mode but feels
  785. // heavy-handed. Is it clobbering something important?
  786. // [bjg 6/13/07]
  787. case WM_KEYUP:
  788. case WM_KEYDOWN:
  789. case WM_SYSKEYUP:
  790. case WM_SYSKEYDOWN:
  791. Dispatch(DelayedDispatch,hWnd,message,wParam,lParam);
  792. return 0;
  793. }
  794. // Queue up for later and invoke the Windows default handler.
  795. Dispatch(DelayedDispatch,hWnd,message,wParam,lParam);
  796. return DefWindowProc(hWnd, message, wParam, lParam);
  797. }
  798. void Win32Window::defaultRender()
  799. {
  800. // Get Window Device Context
  801. HDC logoDC = GetDC(mWindowHandle);
  802. // Get Window Rectangle
  803. RECT lRect;
  804. GetClientRect(mWindowHandle,&lRect);
  805. // Fill with AppWorkspace color
  806. FillRect( logoDC, &lRect, (HBRUSH)GetSysColorBrush(COLOR_APPWORKSPACE) );
  807. // Release Device Context
  808. ReleaseDC(mWindowHandle,logoDC);
  809. }
  810. //-----------------------------------------------------------------------------
  811. // Accelerators
  812. //-----------------------------------------------------------------------------
  813. void Win32Window::addAccelerator(Accelerator &accel)
  814. {
  815. ACCEL winAccel;
  816. winAccel.fVirt = FVIRTKEY;
  817. winAccel.cmd = accel.mID;
  818. if(accel.mDescriptor.flags & SI_SHIFT)
  819. winAccel.fVirt |= FSHIFT;
  820. if(accel.mDescriptor.flags & SI_CTRL)
  821. winAccel.fVirt |= FCONTROL;
  822. if(accel.mDescriptor.flags & SI_ALT)
  823. winAccel.fVirt |= FALT;
  824. winAccel.key = TranslateKeyCodeToOS(accel.mDescriptor.eventCode);
  825. for(WinAccelList::iterator i = mWinAccelList.begin();i != mWinAccelList.end();++i)
  826. {
  827. if(i->cmd == winAccel.cmd)
  828. {
  829. // Already in list, just update it
  830. i->fVirt = winAccel.fVirt;
  831. i->key = winAccel.key;
  832. return;
  833. }
  834. if(i->fVirt == winAccel.fVirt && i->key == winAccel.key)
  835. {
  836. // Existing accelerator in list, don't add this one
  837. return;
  838. }
  839. }
  840. mWinAccelList.push_back(winAccel);
  841. }
  842. void Win32Window::removeAccelerator(Accelerator &accel)
  843. {
  844. for(WinAccelList::iterator i = mWinAccelList.begin();i != mWinAccelList.end();++i)
  845. {
  846. if(i->cmd == accel.mID)
  847. {
  848. mWinAccelList.erase(i);
  849. return;
  850. }
  851. }
  852. }
  853. //-----------------------------------------------------------------------------
  854. static bool isMenuItemIDEnabled(HMENU menu, U32 id)
  855. {
  856. S32 numItems = GetMenuItemCount(menu);
  857. for(S32 i = 0;i < numItems;i++)
  858. {
  859. MENUITEMINFOA mi;
  860. mi.cbSize = sizeof(mi);
  861. mi.fMask = MIIM_ID|MIIM_STATE|MIIM_SUBMENU|MIIM_DATA;
  862. if(GetMenuItemInfoA(menu, i, TRUE, &mi))
  863. {
  864. if(mi.fMask & MIIM_ID && mi.wID == id)
  865. {
  866. // This is an item on this menu
  867. return (mi.fMask & MIIM_STATE) && ! (mi.fState & MFS_DISABLED);
  868. }
  869. if((mi.fMask & MIIM_SUBMENU) && mi.hSubMenu != 0 && (mi.fMask & MIIM_DATA) && mi.dwItemData != 0)
  870. {
  871. // This is a submenu, if it can handle this ID then recurse to find correct state
  872. PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
  873. if(mnu->canHandleID(id))
  874. return isMenuItemIDEnabled(mi.hSubMenu, id);
  875. }
  876. }
  877. }
  878. return false;
  879. }
  880. bool Win32Window::isAccelerator(const InputEventInfo &info)
  881. {
  882. U32 virt;
  883. virt = FVIRTKEY;
  884. if(info.modifier & SI_SHIFT)
  885. virt |= FSHIFT;
  886. if(info.modifier & SI_CTRL)
  887. virt |= FCONTROL;
  888. if(info.modifier & SI_ALT)
  889. virt |= FALT;
  890. U8 keyCode = TranslateKeyCodeToOS(info.objInst);
  891. for(S32 i = 0;i < mWinAccelList.size();++i)
  892. {
  893. const ACCEL &accel = mWinAccelList[i];
  894. if(accel.key == keyCode && accel.fVirt == virt && isMenuItemIDEnabled(getMenuHandle(), accel.cmd))
  895. return true;
  896. }
  897. return false;
  898. }
  899. //-----------------------------------------------------------------------------
  900. void Win32Window::addAccelerators(AcceleratorList &list)
  901. {
  902. if(mAccelHandle)
  903. {
  904. DestroyAcceleratorTable(mAccelHandle);
  905. mAccelHandle = NULL;
  906. }
  907. for(AcceleratorList::iterator i = list.begin();i != list.end();++i)
  908. {
  909. addAccelerator(*i);
  910. }
  911. if(mWinAccelList.size() > 0)
  912. mAccelHandle = CreateAcceleratorTable(&mWinAccelList[0], mWinAccelList.size());
  913. }
  914. void Win32Window::removeAccelerators(AcceleratorList &list)
  915. {
  916. if(mAccelHandle)
  917. {
  918. DestroyAcceleratorTable(mAccelHandle);
  919. mAccelHandle = NULL;
  920. }
  921. for(AcceleratorList::iterator i = list.begin();i != list.end();++i)
  922. {
  923. removeAccelerator(*i);
  924. }
  925. if(mWinAccelList.size() > 0)
  926. mAccelHandle = CreateAcceleratorTable(mWinAccelList.address(), mWinAccelList.size());
  927. }
  928. bool Win32Window::translateMessage(MSG &msg)
  929. {
  930. if(mAccelHandle == NULL || mWindowHandle == NULL || !mEnableAccelerators)
  931. return false;
  932. S32 ret = TranslateAccelerator(mWindowHandle, mAccelHandle, &msg);
  933. return ret != 0;
  934. }
  935. //-----------------------------------------------------------------------------
  936. // Mouse Locking
  937. //-----------------------------------------------------------------------------
  938. void Win32Window::setMouseLocked( bool enable )
  939. {
  940. if (mOffscreenRender)
  941. return;
  942. // Maintain a good state without unnecessary
  943. // cursor hides/modifications
  944. if( enable && mMouseLocked && mShouldLockMouse )
  945. return;
  946. else if(!enable && !mMouseLocked && !mShouldLockMouse )
  947. return;
  948. // Need to be focused to enable mouse lock
  949. // but we can disable it no problem if we're
  950. // not focused
  951. if( !isFocused() && enable )
  952. {
  953. mShouldLockMouse = enable;
  954. return;
  955. }
  956. // Set Flag
  957. mMouseLocked = enable;
  958. if( enable )
  959. {
  960. getCursorPosition( mMouseLockPosition );
  961. RECT r;
  962. GetWindowRect(getHWND(), &r);
  963. // Hide the cursor before it's moved
  964. setCursorVisible( false );
  965. // We have to nudge the cursor clip rect in a bit so we don't go out
  966. // side the bounds of the window... We'll just do it by 32 in all
  967. // directions, which will break for very small windows (< 200x200 or so)
  968. // but otherwise won't matter.
  969. RECT rCopy = r;
  970. rCopy.top += 32; rCopy.bottom -= 64;
  971. rCopy.left += 32; rCopy.right -= 64;
  972. ClipCursor(&rCopy);
  973. S32 centerX = (r.right + r.left) >> 1;
  974. S32 centerY = ((r.bottom + r.top) >> 1);
  975. // Consume all existing mouse events and those posted to our own dispatch queue
  976. MSG msg;
  977. PeekMessage( &msg, 0,WM_MOUSEFIRST,WM_MOUSELAST , PM_QS_POSTMESSAGE | PM_NOYIELD | PM_REMOVE );
  978. RemoveMessages( NULL, WM_MOUSEMOVE, WM_MOUSEMOVE );
  979. // Set the CursorPos
  980. SetCursorPos(centerX, centerY);
  981. // reset should lock flag
  982. mShouldLockMouse = true;
  983. }
  984. else
  985. {
  986. // This belongs before the unlock code
  987. mShouldLockMouse = false;
  988. ClipCursor(NULL);
  989. setCursorPosition( mMouseLockPosition.x,mMouseLockPosition.y );
  990. // Consume all existing mouse events and those posted to our own dispatch queue
  991. MSG msg;
  992. PeekMessage( &msg, NULL,WM_MOUSEFIRST,WM_MOUSELAST , PM_QS_POSTMESSAGE | PM_NOYIELD | PM_REMOVE );
  993. RemoveMessages( NULL, WM_MOUSEMOVE, WM_MOUSEMOVE );
  994. // Show the Cursor
  995. setCursorVisible( true );
  996. }
  997. }
  998. const UTF16 *Win32Window::getWindowClassName()
  999. {
  1000. return _MainWindowClassName;
  1001. }
  1002. const UTF16 *Win32Window::getCurtainWindowClassName()
  1003. {
  1004. return _CurtainWindowClassName;
  1005. }
  1006. #endif