macWindow.mm
Engine/source/windowManager/mac/macWindow.mm
Classes:
class
Detailed Description
Public Functions
torque_setsafariwindow(NSWindow * window, S32 x, S32 y, S32 width, S32 height)
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#include <Cocoa/Cocoa.h> 25#include "windowManager/mac/macWindow.h" 26#include "windowManager/mac/macView.h" 27 28#include "console/console.h" 29 30MacWindow::SafariWindowInfo* MacWindow::sSafariWindowInfo = NULL; 31MacWindow* MacWindow::sInstance = NULL; 32 33@interface SafariBrowserWindow : NSWindow 34{ 35} 36@end 37 38@implementation SafariBrowserWindow 39 40// Windows created with NSBorderlessWindowMask normally can't be key, but we want ours to be 41- (BOOL) canBecomeKeyWindow 42{ 43 return YES; 44} 45 46@end 47 48 49MacWindow::MacWindow(U32 windowId, const char* windowText, Point2I clientExtent) 50{ 51 mMouseLocked = false; 52 mShouldMouseLock = false; 53 mTitle = NULL; 54 mMouseCaptured = false; 55 56 mCocoaWindow = NULL; 57 mCursorController = new MacCursorController( this ); 58 mOwningWindowManager = NULL; 59 60 mFullscreen = false; 61 mShouldFullscreen = false; 62 mDefaultDisplayMode = NULL; 63 64 mSkipMouseEvents = 0; 65 66 mDisplay = kCGDirectMainDisplay; 67 mMainDisplayBounds = mDisplayBounds = CGDisplayBounds(mDisplay); 68 69 mWindowId = windowId; 70 _initCocoaWindow(windowText, clientExtent); 71 72 appEvent.notify(this, &MacWindow::_onAppEvent); 73 74 sInstance = this; 75} 76 77MacWindow::~MacWindow() 78{ 79 if(mFullscreen) 80 _setFullscreen(false); 81 82 appEvent.remove(this, &MacWindow::_onAppEvent); 83 84 //ensure our view isn't the delegate 85 [NSApp setDelegate:nil]; 86 87 if( mCocoaWindow ) 88 { 89 NSWindow* window = mCocoaWindow; 90 _disassociateCocoaWindow(); 91 92 [ window close ]; 93 } 94 95 appEvent.trigger(mWindowId, LoseFocus); 96 appEvent.trigger(mWindowId, WindowDestroy); 97 98 mOwningWindowManager->_removeWindow(this); 99 100 setSafariWindow(NULL); 101 102 sInstance = NULL; 103} 104 105extern "C" 106{ 107 108void torque_setsafariwindow( NSWindow *window, S32 x, S32 y, S32 width, S32 height) 109{ 110 MacWindow::setSafariWindow(window, x, y, width, height); 111} 112 113} 114 115void MacWindow::hideBrowserWindow(bool hide) 116{ 117 if (sSafariWindowInfo && sInstance && sInstance->mCocoaWindow) 118 { 119 if (hide) 120 { 121 if (sSafariWindowInfo && sSafariWindowInfo->safariWindow) 122 [sSafariWindowInfo->safariWindow removeChildWindow: sInstance->mCocoaWindow]; 123 124 sInstance->hide(); 125 } 126 else 127 { 128 129 if (sSafariWindowInfo && sSafariWindowInfo->safariWindow) 130 [sSafariWindowInfo->safariWindow addChildWindow: sInstance->mCocoaWindow ordered:NSWindowAbove]; 131 132 sInstance->show(); 133 } 134 } 135} 136 137void MacWindow::setSafariWindow(NSWindow *window, S32 x, S32 y, S32 width, S32 height ) 138{ 139 if (!window) 140 { 141 hideBrowserWindow(true); 142 143 if (sSafariWindowInfo) 144 delete sSafariWindowInfo; 145 146 sSafariWindowInfo = NULL; 147 148 return; 149 } 150 151 if (!sSafariWindowInfo) 152 { 153 sSafariWindowInfo = new SafariWindowInfo; 154 sSafariWindowInfo->safariWindow = window; 155 sSafariWindowInfo->width = width; 156 sSafariWindowInfo->height = height; 157 sSafariWindowInfo->x = x; 158 sSafariWindowInfo->y = y; 159 if (sInstance && sInstance->mCocoaWindow) 160 { 161 [window addChildWindow: sInstance->mCocoaWindow ordered:NSWindowAbove]; 162 hideBrowserWindow(false); 163 } 164 } 165 else 166 { 167 sSafariWindowInfo->width = width; 168 sSafariWindowInfo->height = height; 169 sSafariWindowInfo->x = x; 170 sSafariWindowInfo->y = y; 171 } 172 173 if (sInstance && sInstance->mCocoaWindow) 174 { 175 //update position 176 177 NSRect frame = [sSafariWindowInfo->safariWindow frame]; 178 179 NSPoint o = { (float)sSafariWindowInfo->x, frame.size.height - sSafariWindowInfo->y }; 180 NSPoint p = [sSafariWindowInfo->safariWindow convertBaseToScreen: o]; 181 182 NSRect contentRect = NSMakeRect(p.x, p.y - sSafariWindowInfo->height, sSafariWindowInfo->width,sSafariWindowInfo->height); 183 184 // we have to set display to NO when resizing otherwise get hangs, perhaps add delegate to SafariBrowserWindow class? 185 [sInstance->mCocoaWindow setFrame:contentRect display:NO]; 186 187 } 188 189} 190 191void MacWindow::_initCocoaWindow(const char* windowText, Point2I clientExtent) 192{ 193 // TODO: cascade windows on screen? 194 195 // create the window 196 NSRect contentRect; 197 U32 style; 198 199 if (sSafariWindowInfo) 200 { 201 202 NSRect frame = [sSafariWindowInfo->safariWindow frame]; 203 204 NSPoint o = { (float)sSafariWindowInfo->x, frame.size.height - sSafariWindowInfo->y }; 205 206 NSPoint p = [sSafariWindowInfo->safariWindow convertBaseToScreen: o]; 207 208 contentRect = NSMakeRect(0, 0, sSafariWindowInfo->width,sSafariWindowInfo->height); 209 210 style = NSBorderlessWindowMask; 211 212 mCocoaWindow = [[SafariBrowserWindow alloc] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:YES screen:nil]; 213 214 [mCocoaWindow setFrameTopLeftPoint: p]; 215 216 [sSafariWindowInfo->safariWindow addChildWindow: mCocoaWindow ordered:NSWindowAbove]; 217 218 // necessary to accept mouseMoved events 219 [mCocoaWindow setAcceptsMouseMovedEvents:YES]; 220 } 221 else 222 { 223 224 contentRect = NSMakeRect(0,0,clientExtent.x, clientExtent.y); 225 226 style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask; 227 228 mCocoaWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:YES screen:nil]; 229 if(windowText) 230 [mCocoaWindow setTitle: [NSString stringWithUTF8String: windowText]]; 231 232 // necessary to accept mouseMoved events 233 [mCocoaWindow setAcceptsMouseMovedEvents:YES]; 234 235 // correctly position the window on screen 236 [mCocoaWindow center]; 237 238 } 239 240 // create the opengl view. we don't care about its pixel format, because we 241 // will be replacing its context with another one. 242 GGMacView* view = [[GGMacView alloc] initWithFrame:contentRect pixelFormat:[NSOpenGLView defaultPixelFormat]]; 243 [view setTorqueWindow:this]; 244 [mCocoaWindow setContentView:view]; 245 [mCocoaWindow setDelegate:view]; 246 247} 248 249void MacWindow::_disassociateCocoaWindow() 250{ 251 if( !mCocoaWindow ) 252 return; 253 254 [mCocoaWindow setContentView:nil]; 255 [mCocoaWindow setDelegate:nil]; 256 257 if (sSafariWindowInfo) 258 [sSafariWindowInfo->safariWindow removeChildWindow: mCocoaWindow]; 259 260 mCocoaWindow = NULL; 261} 262 263void MacWindow::_setVideoMode(const GFXVideoMode &mode) 264{ 265 mCurrentMode = mode; 266 setSize(mCurrentMode.resolution); 267 268 if(mTarget.isValid()) 269 mTarget->resetMode(); 270 271 _setFullscreen(mCurrentMode.fullScreen); 272} 273 274void MacWindow::_onAppEvent(WindowId, S32 evt) 275{ 276 if(evt == LoseFocus && isFullscreen()) 277 { 278 mShouldFullscreen = true; 279 GFXVideoMode mode = mCurrentMode; 280 mode.fullScreen = false; 281 setVideoMode(mode); 282 } 283 284 if(evt == GainFocus && !isFullscreen() && mShouldFullscreen) 285 { 286 mShouldFullscreen = false; 287 GFXVideoMode mode = mCurrentMode; 288 mode.fullScreen = true; 289 setVideoMode(mode); 290 } 291} 292 293void MacWindow::_setFullscreen(bool fullScreen) 294{ 295 if(mFullscreen == fullScreen) 296 return; 297 298 mFullscreen = fullScreen; 299 300 if(mFullscreen) 301 { 302 Con::printf("Capturing display %x", mDisplay); 303 CGDisplayCapture(mDisplay); 304 [mCocoaWindow setAlphaValue:0.0f]; 305 } 306 else 307 { 308 if(mDefaultDisplayMode) 309 { 310 Con::printf("Restoring default display mode... width: %i height: %i bpp: %i", [[mDefaultDisplayMode valueForKey:@"Width"] intValue], 311 [[mDefaultDisplayMode valueForKey:@"Height"] intValue], [[mDefaultDisplayMode valueForKey:@"BitsPerPixel"] intValue]); 312 CGDisplaySwitchToMode(mDisplay, (CFDictionaryRef)mDefaultDisplayMode); 313 mDisplayBounds = CGDisplayBounds(mDisplay); 314 if(mDisplay == kCGDirectMainDisplay) 315 mMainDisplayBounds = mDisplayBounds; 316 } 317 318 Con::printf("Releasing display %x", mDisplay); 319 CGDisplayRelease(mDisplay); 320 [mCocoaWindow setAlphaValue:1.0f]; 321 mDefaultDisplayMode = NULL; 322 } 323} 324 325void* MacWindow::getPlatformDrawable() const 326{ 327 return [mCocoaWindow contentView]; 328} 329 330void MacWindow::show() 331{ 332 [mCocoaWindow makeKeyAndOrderFront:nil]; 333 [mCocoaWindow makeFirstResponder:[mCocoaWindow contentView]]; 334 appEvent.trigger(getWindowId(), WindowShown); 335 appEvent.trigger(getWindowId(), GainFocus); 336} 337 338void MacWindow::close() 339{ 340 [mCocoaWindow close]; 341 appEvent.trigger(mWindowId, LoseFocus); 342 appEvent.trigger(mWindowId, WindowDestroy); 343 344 mOwningWindowManager->_removeWindow(this); 345 346 delete this; 347} 348 349void MacWindow::hide() 350{ 351 [mCocoaWindow orderOut:nil]; 352 appEvent.trigger(getWindowId(), WindowHidden); 353} 354 355void MacWindow::setDisplay(CGDirectDisplayID display) 356{ 357 mDisplay = display; 358 mDisplayBounds = CGDisplayBounds(mDisplay); 359} 360 361PlatformWindow* MacWindow::getNextWindow() const 362{ 363 return mNextWindow; 364} 365 366bool MacWindow::setSize(const Point2I &newSize) 367{ 368 if (sSafariWindowInfo) 369 return true; 370 371 NSSize newExtent = {newSize.x, newSize.y}; 372 [mCocoaWindow setContentSize:newExtent]; 373 [mCocoaWindow center]; 374 return true; 375} 376 377void MacWindow::setClientExtent( const Point2I newExtent ) 378{ 379 if(!mFullscreen) 380 { 381 // Set the Client Area Extent (Resolution) of this window 382 NSSize newSize = {newExtent.x, newExtent.y}; 383 [mCocoaWindow setContentSize:newSize]; 384 } 385 else 386 { 387 // In fullscreen we have to resize the monitor (it'll be good to change it back too...) 388 if(!mDefaultDisplayMode) 389 mDefaultDisplayMode = (NSDictionary*)CGDisplayCurrentMode(mDisplay); 390 391 NSDictionary* newMode = (NSDictionary*)CGDisplayBestModeForParameters(mDisplay, 32, newExtent.x, newExtent.y, NULL); 392 Con::printf("Switching to new display mode... width: %i height: %i bpp: %i", 393 [[newMode valueForKey:@"Width"] intValue], [[newMode valueForKey:@"Height"] intValue], [[newMode valueForKey:@"BitsPerPixel"] intValue]); 394 CGDisplaySwitchToMode(mDisplay, (CFDictionaryRef)newMode); 395 mDisplayBounds = CGDisplayBounds(mDisplay); 396 if(mDisplay == kCGDirectMainDisplay) 397 mMainDisplayBounds = mDisplayBounds; 398 } 399} 400 401const Point2I MacWindow::getClientExtent() 402{ 403 if(!mFullscreen) 404 { 405 // Get the Client Area Extent (Resolution) of this window 406 NSSize size = [[mCocoaWindow contentView] frame].size; 407 return Point2I(size.width, size.height); 408 } 409 else 410 { 411 return Point2I(mDisplayBounds.size.width, mDisplayBounds.size.height); 412 } 413} 414 415void MacWindow::setBounds( const RectI &newBounds ) 416{ 417 NSRect newFrame = NSMakeRect(newBounds.point.x, newBounds.point.y, newBounds.extent.x, newBounds.extent.y); 418 [mCocoaWindow setFrame:newFrame display:YES]; 419} 420 421const RectI MacWindow::getBounds() const 422{ 423 if(!mFullscreen) 424 { 425 // Get the position and size (fullscreen windows are always at (0,0)). 426 NSRect frame = [mCocoaWindow frame]; 427 return RectI(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); 428 } 429 else 430 { 431 return RectI(0, 0, mDisplayBounds.size.width, mDisplayBounds.size.height); 432 } 433} 434 435void MacWindow::setPosition( const Point2I newPosition ) 436{ 437 NSScreen *screen = [mCocoaWindow screen]; 438 NSRect screenFrame = [screen frame]; 439 440 NSPoint pos = {newPosition.x, newPosition.y + screenFrame.size.height}; 441 [mCocoaWindow setFrameTopLeftPoint: pos]; 442} 443 444const Point2I MacWindow::getPosition() 445{ 446 NSScreen *screen = [mCocoaWindow screen]; 447 NSRect screenFrame = [screen frame]; 448 NSRect frame = [mCocoaWindow frame]; 449 450 return Point2I(frame.origin.x, screenFrame.size.height - (frame.origin.y + frame.size.height)); 451} 452 453void MacWindow::centerWindow() 454{ 455 [mCocoaWindow center]; 456} 457 458Point2I MacWindow::clientToScreen( const Point2I& pos ) 459{ 460 NSPoint p = { pos.x, pos.y }; 461 462 p = [ mCocoaWindow convertBaseToScreen: p ]; 463 return Point2I( p.x, p.y ); 464} 465 466Point2I MacWindow::screenToClient( const Point2I& pos ) 467{ 468 NSPoint p = { pos.x, pos.y }; 469 470 p = [ mCocoaWindow convertScreenToBase: p ]; 471 return Point2I( p.x, p.y ); 472} 473 474bool MacWindow::isFocused() 475{ 476 return [mCocoaWindow isKeyWindow]; 477} 478 479bool MacWindow::isOpen() 480{ 481 // Maybe check if _window != NULL ? 482 return true; 483} 484 485bool MacWindow::isVisible() 486{ 487 return !isMinimized() && ([mCocoaWindow isVisible] == YES); 488} 489 490void MacWindow::setFocus() 491{ 492 [mCocoaWindow makeKeyAndOrderFront:nil]; 493} 494 495void MacWindow::signalGainFocus() 496{ 497 if(isFocused()) 498 [[mCocoaWindow delegate] performSelector:@selector(signalGainFocus)]; 499} 500 501void MacWindow::minimize() 502{ 503 if(!isVisible()) 504 return; 505 506 [mCocoaWindow miniaturize:nil]; 507 appEvent.trigger(getWindowId(), WindowHidden); 508} 509 510void MacWindow::maximize() 511{ 512 if(!isVisible()) 513 return; 514 515 // GFX2_RENDER_MERGE 516 //[mCocoaWindow miniaturize:nil]; 517 //appEvent.trigger(getWindowId(), WindowHidden); 518} 519 520void MacWindow::restore() 521{ 522 if(!isMinimized()) 523 return; 524 525 [mCocoaWindow deminiaturize:nil]; 526 appEvent.trigger(getWindowId(), WindowShown); 527} 528 529bool MacWindow::isMinimized() 530{ 531 return [mCocoaWindow isMiniaturized] == YES; 532} 533 534bool MacWindow::isMaximized() 535{ 536 return false; 537} 538 539void MacWindow::clearFocus() 540{ 541 // Clear the focus state for this Window. 542 // If the Window does not have focus, nothing happens. 543 // If the Window has focus, it relinquishes it's focus to the Operating System 544 545 // TODO: find out if we can do anything correct here. We are instructed *not* to call [NSWindow resignKeyWindow], and we don't necessarily have another window to assign as key. 546} 547 548bool MacWindow::setCaption(const char* windowText) 549{ 550 mTitle = windowText; 551 [mCocoaWindow setTitle:[NSString stringWithUTF8String:mTitle]]; 552 return true; 553} 554 555void MacWindow::_doMouseLockNow() 556{ 557 if(!isVisible()) 558 return; 559 560 if(mShouldMouseLock == mMouseLocked && mMouseLocked != isCursorVisible()) 561 return; 562 563 if(mShouldMouseLock) 564 _dissociateMouse(); 565 else 566 _associateMouse(); 567 568 // hide the cursor if we're locking, show it if we're unlocking 569 setCursorVisible(!shouldLockMouse()); 570 571 mMouseLocked = mShouldMouseLock; 572 573 return; 574} 575 576void MacWindow::_associateMouse() 577{ 578 CGAssociateMouseAndMouseCursorPosition(true); 579} 580 581void MacWindow::_dissociateMouse() 582{ 583 _centerMouse(); 584 CGAssociateMouseAndMouseCursorPosition(false); 585} 586 587void MacWindow::_centerMouse() 588{ 589 NSRect frame = [mCocoaWindow frame]; 590 591 // Deal with the y flip (really fun when more than one monitor is involved) 592 F32 offsetY = mMainDisplayBounds.size.height - mDisplayBounds.size.height; 593 frame.origin.y = (mDisplayBounds.size.height + offsetY) - (S32)frame.origin.y - (S32)frame.size.height; 594 mCursorController->setCursorPosition(frame.origin.x + frame.size.width / 2, frame.origin.y + frame.size.height / 2); 595} 596