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