macWindow.mm 16 KB

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