Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3828 lines
121 KiB

  1. //========= Copyright 1996-2009, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Defines a group of app systems that all have the same lifetime
  4. // that need to be connected/initialized, etc. in a well-defined order
  5. //
  6. // $Revision: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include <Cocoa/Cocoa.h>
  10. #include <OpenGL/OpenGL.h>
  11. #include <OpenGL/gl.h>
  12. #include <OpenGL/glext.h>
  13. #include <IOKit/IOKitLib.h>
  14. #undef MIN
  15. #undef MAX
  16. #define DONT_DEFINE_BOOL // Don't define BOOL!
  17. #include "tier0/threadtools.h"
  18. #include "tier0/icommandline.h"
  19. #include "tier0/fasttimer.h"
  20. #include "tier0/dynfunction.h"
  21. #include "tier1/interface.h"
  22. #include "tier1/strtools.h"
  23. #include "tier1/utllinkedlist.h"
  24. #include "togl/rendermechanism.h"
  25. #include "appframework/ilaunchermgr.h" // gets pulled in from glmgr.h
  26. #include "appframework/iappsystemgroup.h"
  27. #include "inputsystem/ButtonCode.h"
  28. #ifdef GLMPRINTF
  29. #undef GLMPRINTF
  30. #endif
  31. #if GLMDEBUG
  32. #define GLMPRINTF(args) printf args
  33. #else
  34. #define GLMPRINTF(args)
  35. #endif
  36. @class CocoaBridge;
  37. @class CocoaViewBridge;
  38. class CCocoaThreadMsg;
  39. COpenGLEntryPoints *gGL = NULL;
  40. // ------------------------------------------------------------------------------------ //
  41. // some Carbon stuff we're using
  42. // ------------------------------------------------------------------------------------ //
  43. extern "C" uint GetCurrentKeyModifiers( void );
  44. enum ECarbonModKeyIndex
  45. {
  46. EcmdKeyBit = 8, /* command key down?*/
  47. EshiftKeyBit = 9, /* shift key down?*/
  48. EalphaLockBit = 10, /* alpha lock down?*/
  49. EoptionKeyBit = 11, /* option key down?*/
  50. EcontrolKeyBit = 12 /* control key down?*/
  51. };
  52. enum ECarbonModKeyMask
  53. {
  54. EcmdKey = 1 << EcmdKeyBit,
  55. EshiftKey = 1 << EshiftKeyBit,
  56. EalphaLock = 1 << EalphaLockBit,
  57. EoptionKey = 1 << EoptionKeyBit,
  58. EcontrolKey = 1 << EcontrolKeyBit
  59. };
  60. enum {
  61. kUIModeNormal = 0,
  62. kUIModeContentSuppressed = 1,
  63. kUIModeContentHidden = 2,
  64. kUIModeAllSuppressed = 4,
  65. kUIModeAllHidden = 3,
  66. };
  67. typedef UInt32 SystemUIMode;
  68. enum {
  69. kUIOptionAutoShowMenuBar = 1 << 0,
  70. kUIOptionDisableAppleMenu = 1 << 2,
  71. kUIOptionDisableProcessSwitch = 1 << 3,
  72. kUIOptionDisableForceQuit = 1 << 4,
  73. kUIOptionDisableSessionTerminate = 1 << 5,
  74. kUIOptionDisableHide = 1 << 6
  75. };
  76. typedef OptionBits SystemUIOptions;
  77. extern "C" OSStatus SetSystemUIMode ( SystemUIMode inMode, SystemUIOptions inOptions );
  78. void SetFullscreenUIMode( bool on )
  79. {
  80. if (on)
  81. {
  82. SetSystemUIMode( kUIModeAllHidden, (kUIOptionAutoShowMenuBar | /* kUIOptionDisableProcessSwitch |*/ kUIOptionDisableHide) );
  83. }
  84. else
  85. {
  86. SetSystemUIMode( kUIModeNormal, (SystemUIOptions)0);
  87. }
  88. }
  89. extern "C" uint GetCurrentEventButtonState( void );
  90. int GetCurrentMouseButtonState( void )
  91. {
  92. // see http://lists.apple.com/archives/cocoa-dev/2010/Feb/msg01193.html
  93. // and http://serenity.uncc.edu/web/ADC/2005/Developer_DVD_Series/April/ADC%20Reference%20Library/documentation/Carbon/Conceptual/Carbon_Event_Manager/Tasks/chapter_18_section_10.html
  94. if ([NSEvent respondsToSelector:@selector(pressedMouseButtons)])
  95. return (int)[NSEvent pressedMouseButtons];
  96. else
  97. return (int)GetCurrentEventButtonState();
  98. }
  99. // ------------------------------------------------------------------------------------ //
  100. // Couple of functions for making and freeing AR pools
  101. // ------------------------------------------------------------------------------------ //
  102. void *macMakeAutoreleasePool(void)
  103. {
  104. return [[NSAutoreleasePool alloc] init];
  105. }
  106. void macReleaseAutoreleasePool(void *_pool)
  107. {
  108. NSAutoreleasePool *pool = (NSAutoreleasePool *) _pool;
  109. [pool release];
  110. }
  111. // NSWindow subclass
  112. bool s_bBlockWarpCursor = false;
  113. @interface AppWindow: NSWindow
  114. {
  115. uint dummy;
  116. }
  117. -(BOOL)canBecomeKeyWindow;
  118. -(BOOL)canBecomeMainWindow;
  119. -(void)becomeKeyWindow;
  120. -(void)sendEvent:(NSEvent *)event;
  121. -(void)resignMainWindow;
  122. @end
  123. @implementation AppWindow
  124. -(BOOL)canBecomeKeyWindow
  125. {
  126. return YES;
  127. }
  128. -(BOOL)canBecomeMainWindow
  129. {
  130. return YES;
  131. }
  132. -(void)resignMainWindow
  133. {
  134. [super resignMainWindow];
  135. s_bBlockWarpCursor = true;
  136. }
  137. -(void)becomeKeyWindow
  138. {
  139. NSEvent *event = [self currentEvent];
  140. if ( [event type] == NSLeftMouseDown && [event window] == self )
  141. {
  142. NSPoint pt = [self mouseLocationOutsideOfEventStream];
  143. NSRect windowFrame = [self frame];
  144. if ( pt.y > ( windowFrame.size.height - 21 ) && pt.y < windowFrame.size.height )
  145. s_bBlockWarpCursor = true;
  146. else
  147. s_bBlockWarpCursor = false;
  148. }
  149. else
  150. s_bBlockWarpCursor = false;
  151. [super becomeKeyWindow];
  152. }
  153. -(void)sendEvent:(NSEvent *)event
  154. {
  155. [super sendEvent:event];
  156. if ( s_bBlockWarpCursor )
  157. {
  158. if ( [event type] == NSLeftMouseUp )
  159. s_bBlockWarpCursor = false;
  160. else if ( [event type] == NSLeftMouseDown && [event window] == self )
  161. {
  162. NSPoint pt = [self mouseLocationOutsideOfEventStream];
  163. NSRect windowFrame = [self frame];
  164. if ( pt.y > ( windowFrame.size.height - 21 ) && pt.y < windowFrame.size.height )
  165. s_bBlockWarpCursor = true;
  166. else
  167. s_bBlockWarpCursor = false;
  168. }
  169. }
  170. }
  171. @end
  172. //===============================================================================
  173. void __checkgl__( void )
  174. {
  175. #if GLMDEBUG
  176. GLenum errorcode = (GLenum)glGetError();
  177. if (errorcode != GL_NO_ERROR)
  178. {
  179. Debugger();
  180. printf("\nGL Error %d",errorcode);
  181. }
  182. #endif
  183. }
  184. // some helper functions, relocated out of GLM since they are used here
  185. // this one makes a new context
  186. bool GLMDetectSLGU( void )
  187. {
  188. CGLError cgl_error = (CGLError)0;
  189. bool result = false;
  190. CGLContextObj oldctx = CGLGetCurrentContext();
  191. static CGLPixelFormatAttribute attribs[] =
  192. {
  193. kCGLPFADoubleBuffer,
  194. kCGLPFANoRecovery,
  195. kCGLPFAAccelerated,
  196. kCGLPFADepthSize,
  197. (CGLPixelFormatAttribute)0,
  198. kCGLPFAColorSize,
  199. (CGLPixelFormatAttribute)32,
  200. (CGLPixelFormatAttribute)0 // list term
  201. };
  202. CGLPixelFormatObj pixfmtobj = NULL;
  203. GLint npix;
  204. CGLContextObj ctxobj = NULL;
  205. cgl_error = CGLChoosePixelFormat( attribs, &pixfmtobj, &npix );
  206. if (!cgl_error)
  207. {
  208. // got pixel format, make a context
  209. cgl_error = CGLCreateContext( pixfmtobj, NULL, &ctxobj );
  210. if (!cgl_error)
  211. {
  212. CGLSetCurrentContext( ctxobj );
  213. // now do the test
  214. _CGLContextParameter kCGLCPGCDMPEngine = ((_CGLContextParameter)1314);
  215. GLint dummyval = 0;
  216. cgl_error = CGLGetParameter( CGLGetCurrentContext(), kCGLCPGCDMPEngine, &dummyval );
  217. result = (!cgl_error);
  218. // all done, go back to old context, and destroy the temp one
  219. CGLSetCurrentContext( oldctx );
  220. CGLDestroyContext( ctxobj );
  221. }
  222. // destroy the pixel format obj
  223. CGLDestroyPixelFormat( pixfmtobj );
  224. }
  225. return result;
  226. }
  227. bool GLMDetectScaledResolveMode( uint osComboVersion, bool hasSLGU )
  228. {
  229. bool result = false;
  230. // note this function assumes a current context on the renderer in question
  231. // and that FB blit and SLGU are present..
  232. if (!hasSLGU)
  233. return false;
  234. if (osComboVersion <= 0x000A0604) // we know no one has it before 10.6.5
  235. return false;
  236. // in 10.6.6 and later, just check for the ext string.
  237. char *gl_ext_string = (char*)glGetString(GL_EXTENSIONS);
  238. // if we failed to init and bind a GL context at all, glGetString can return null
  239. if ( !gl_ext_string )
  240. return false;
  241. result = strstr(gl_ext_string, "GL_EXT_framebuffer_multisample_blit_scaled") != NULL;
  242. // if we didn't find an explicity extension string try to sniff for it
  243. if ( !result )
  244. {
  245. // make two FBO's
  246. GLuint fbos[2];
  247. GLuint rbos[2];
  248. int extent = 64;
  249. // make two render buffers
  250. for( int fbi = 0; fbi < 2; fbi++ )
  251. {
  252. glGenFramebuffersEXT( 1, &fbos[fbi] ); __checkgl__();
  253. glBindFramebufferEXT( fbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT , fbos[fbi] ); __checkgl__();
  254. glGenRenderbuffersEXT( 1, &rbos[fbi] ); __checkgl__();
  255. glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rbos[fbi] ); __checkgl__();
  256. // make it multisampled if 0
  257. if (!fbi)
  258. {
  259. glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, 2, GL_RGBA8, extent,extent ); __checkgl__();
  260. }
  261. else
  262. {
  263. glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_RGBA8, extent,extent ); __checkgl__();
  264. }
  265. // attach it
  266. // #0 gets to be read and multisampled
  267. // #1 gets to be draw and multisampled
  268. glFramebufferRenderbufferEXT( fbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rbos[fbi] ); __checkgl__();
  269. }
  270. // now test
  271. while( glGetError() ) // clear error queue
  272. {
  273. ;
  274. }
  275. // now do the dummy blit
  276. glBlitFramebufferEXT( 0,0,extent,extent, 0,0,extent,extent, GL_COLOR_BUFFER_BIT, XGL_SCALED_RESOLVE_FASTEST_EXT );
  277. // type of error we get back lets us know what the outcome is.
  278. // invalid enum error -> unsupported
  279. // no error or invalid op -> supported
  280. GLenum errorcode = (GLenum)glGetError();
  281. switch(errorcode)
  282. {
  283. // expected outcomes.
  284. // positive
  285. case GL_NO_ERROR:
  286. case GL_INVALID_OPERATION:
  287. result = true; // new scaled resolve detected
  288. break;
  289. default:
  290. result = false; // no scaled resolve
  291. break;
  292. }
  293. // unbind and wipe stuff
  294. glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); __checkgl__();
  295. for( int xfbi = 0; xfbi < 2; xfbi++ )
  296. {
  297. // unbind FBO
  298. glBindFramebufferEXT( xfbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT , 0 ); __checkgl__();
  299. // del FBO and RBO
  300. glDeleteFramebuffersEXT( 1, &fbos[xfbi] ); __checkgl__();
  301. glDeleteRenderbuffersEXT( 1, &rbos[xfbi] ); __checkgl__();
  302. }
  303. }
  304. return result; // no SLGU, no scaled resolve blit even possible
  305. }
  306. void *VoidFnPtrLookup_GlMgr(const char *libname, const char *fn, bool &okay, const bool bRequired, void *fallback)
  307. {
  308. void *retval = NULL;
  309. if ((!okay) && (!bRequired)) // always look up if required (so we get a complete list of crucial missing symbols).
  310. return NULL;
  311. // The SDL path would work on all these platforms, if we were using SDL there, too...
  312. #if defined( LINUX ) || defined( WIN32 )
  313. // SDL does the right thing, so we never need to use tier0 in this case.
  314. retval = SDL_GL_GetProcAddress(fn);
  315. //printf("CDynamicFunctionOpenGL: SDL_GL_GetProcAddress(\"%s\") returned %p\n", fn, retval);
  316. if ((retval == NULL) && (fallback != NULL))
  317. {
  318. //printf("CDynamicFunctionOpenGL: Using fallback %p for \"%s\"\n", fallback, fn);
  319. retval = fallback;
  320. }
  321. #elif defined( OSX )
  322. // there's no glXGetProcAddress() equivalent for Mac OS X...it's just dlopen(), basically. Let tier0 handle that.
  323. retval = VoidFnPtrLookup_Tier0( libname, fn, (void *) fallback);
  324. #else
  325. #error Unimplemented
  326. #endif
  327. // Note that a non-NULL response doesn't mean it's safe to call the function!
  328. // You always have to check that the extension is supported;
  329. // an implementation MAY return NULL in this case, but it doesn't have to (and doesn't, with the DRI drivers).
  330. okay = (okay && (retval != NULL));
  331. if (bRequired && !okay)
  332. {
  333. fprintf( stderr, "Could not find required OpenGL entry point '%s'!\n", fn );
  334. // We can't continue execution, because one or more GL function pointers will be NULL.
  335. Error( "Could not find required OpenGL entry point '%s'!\n", fn);
  336. }
  337. return retval;
  338. }
  339. // ------------------------------------------------------------------------------------ //
  340. // CCocoaMgr class.
  341. // ------------------------------------------------------------------------------------ //
  342. class CCocoaMgr : public ILauncherMgr
  343. {
  344. // Called by the ValveCocoaMain startup code.
  345. public:
  346. CCocoaMgr();
  347. void FinishLaunchingApplication();
  348. // Create the NSApplication object. return false if it was already done.
  349. bool CreateApplicationObject();
  350. void WaitForApplicationToFinishLaunching();
  351. // Called from the Valve main thread. Stops the NSApplication's run loop.
  352. void StopRunLoop();
  353. // Called from the Cocoa thread.
  354. void ProcessMessageInCocoaThread( CCocoaThreadMsg *pMessage );
  355. // If we're in the Cocoa thread, this processes the message directly.
  356. // Otherwise, it sends it in to be processed.
  357. void SendMessageToCocoaThread( CCocoaThreadMsg *pMessage, bool bWaitUntilDone );
  358. // Post an event to the input event queue.
  359. // if debugEvent is true, post it to the debug event queue.
  360. void PostEvent( const CCocoaEvent &theEvent, bool debugEvent=false );
  361. // ask if an event is debug flavor or not.
  362. bool IsDebugEvent( CCocoaEvent& event );
  363. void AccumlateMouseDelta( int32 &x, int32 &y );
  364. void ShowMainWindow();
  365. // ICocoaMgr overrides.
  366. public:
  367. virtual bool CreateGameWindow( const char *pTitle, bool bWindowed, int width, int height );
  368. virtual void DestroyGameWindow();
  369. virtual PseudoNSGLContextPtr GetNSGLContextForWindow( void* nswindow );
  370. virtual int GetEvents( CCocoaEvent *pEvents, int nMaxEventsToReturn, bool debugEvent = false );
  371. virtual void SetCursorPosition( int x, int y );
  372. virtual void* GetWindowRef();
  373. virtual bool Connect( CreateInterfaceFn factory );
  374. virtual void Disconnect() ;
  375. virtual void *QueryInterface( const char *pInterfaceName );
  376. virtual InitReturnVal_t Init() ;
  377. virtual void Shutdown();
  378. virtual void ShowPixels( CShowPixelsParams *params );
  379. virtual void MoveWindow( int x, int y );
  380. virtual void SizeWindow( int width, int tall );
  381. virtual void PumpWindowsMessageLoop();
  382. virtual void WaitUntilUserInput( int msSleepTime );
  383. virtual void GetStackCrawl( CStackCrawlParams *params );
  384. virtual void SetApplicationIcon( const char *pchAppIconFile );
  385. virtual void GetMouseDelta( int &x, int &y, bool bIgnoreNextDelta );
  386. virtual void RenderedSize( uint &width, uint &height, bool set ); // either set or retrieve rendered size value
  387. virtual void DisplayedSize( uint &width, uint &height ); // query backbuffer size (window size whether FS or windowed)
  388. virtual void GetDesiredPixelFormatAttribsAndRendererInfo( uint **ptrOut, uint *countOut, GLMRendererInfoFields *rendInfoOut );
  389. virtual GLMDisplayDB *GetDisplayDB( void );
  390. // does this need to be virtual? don't think so..
  391. NSPoint ScreenCoordsToSourceWindowCoords( NSView *pView, NSPoint screenCoord );
  392. // new to the cocoa launch mgr
  393. virtual void SetWindowFullScreen( bool bFullScreen, int nWidth, int nHeight );
  394. virtual bool IsWindowFullScreen();
  395. virtual void SetForbidMouseGrab( bool bForbidMouseGrab );
  396. virtual double GetPrevGLSwapWindowTime() { return m_flPrevGLSwapWindowTime; }
  397. virtual PseudoGLContextPtr GetGLContextForWindow( void* windowref );
  398. virtual PseudoGLContextPtr GetMainContext();
  399. virtual PseudoGLContextPtr CreateExtraContext();
  400. virtual void DeleteContext( PseudoGLContextPtr hContext );
  401. virtual bool MakeContextCurrent( PseudoGLContextPtr hContext );
  402. virtual void SetMouseVisible( bool bState );
  403. virtual void GetDisplaySize( uint *puiWidth, uint *puiHeight ); // Retrieve the size of the monitor (desktop)
  404. virtual void GetNativeDisplayInfo( int nDisplay, uint &nWidth, uint &nHeight, uint &nRefreshHz );
  405. // Messages piped through from Objective-C objects.
  406. public:
  407. void applicationDidFinishLaunching();
  408. void applicationWillTerminate();
  409. bool BFullScreen() { return m_fsEnable; }
  410. private:
  411. void PostDummyEvent();
  412. bool InternalCreateWindow( const char *pTitle, bool bWindowed, NSRect *frame );
  413. void SetWindowedFrame( NSRect *frame, bool forEffect ); // *frame goes into m_winFrame unless null. If forEffect = true, window adopts m_winFrame and windowed style mask.
  414. void SetFullscreenFrame( NSRect *frame, bool forEffect ); // *frame goes into m_fsFrame unless null. If forEffect = true, window adopts m_fsFrame and adopts fullscreen style mask.
  415. void ClearGLView( void ); // blacken the m_view (NSGLView) and flush it out
  416. private:
  417. CThreadMutex m_CocoaEventsMutex; // use for either queue below
  418. CUtlLinkedList<CCocoaEvent,int> m_CocoaEvents;
  419. CUtlLinkedList<CCocoaEvent,int> m_DebugEvents; // intercepted keys which wil be passed over to GLM
  420. void *m_application;
  421. AppWindow *m_window;
  422. CocoaViewBridge *m_view;
  423. uint m_renderedWidth,m_rendererHeight; // latched from RenderedSize
  424. GLMDisplayDB *m_displayDB;
  425. bool m_leopard; // true if <10.6.3 and we have to do extra work for fullscreen handling
  426. bool m_force_vsync; // true if 10.6.4 + bad NV driver
  427. uint m_chosenRendererIndex; // zero for now.. use this to drive the generation of the PFA next, and to answer the req for renderer info from GLM
  428. uint m_pixelFormatAttribs[20]; // preferred attrib list for window view context and for drawing context in engine thread.
  429. uint m_pixelFormatAttribCount;
  430. NSRect m_winFrame; // where window is when windowed mode (save it before switching to FS)
  431. NSRect m_fsFrame; // where window goes in full screen (set it before switching to FS)
  432. bool m_fsEnable; // are we in fullscreen now?
  433. ThreadId_t m_nRunLoopThreadID;
  434. CThreadEvent m_AppObjectInitialized;
  435. CocoaBridge *m_pCocoaBridge;
  436. CShowPixelsParams m_lastShownPixels; // we may peek at this to infer what the video resolution of the engine is...
  437. NSOpenGLContext *m_showPixelsCtx;
  438. int m_lastKnownSwapInterval; //-1 if unknown, 0/1 otherwise
  439. int m_lastKnownSwapLimit; //-1 if unknown, 0/1 otherwise
  440. int32 m_MouseDeltaX, m_MouseDeltaY;
  441. bool m_bIgnoreNextMouseDelta;
  442. char *m_windowTitle;
  443. public:
  444. int m_frontPushCounter; // if non zero, bump window to front on ShowPixels
  445. double m_flPrevGLSwapWindowTime;
  446. };
  447. int s_windowedStyleMask = NSClosableWindowMask | /*NSResizableWindowMask |*/ NSTexturedBackgroundWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask;
  448. int s_fullscreenStyleMask = NSBorderlessWindowMask;
  449. CCocoaMgr g_CocoaMgr;
  450. ILauncherMgr *g_pCocoaMgr = &g_CocoaMgr;
  451. void* CreateCCocoaMgr()
  452. {
  453. return g_pCocoaMgr;
  454. }
  455. //EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCocoaMgr, ICocoaMgr, COCOAMGR_INTERFACE_VERSION, g_CocoaMgr );
  456. // ------------------------------------------------------------------------------------ //
  457. //
  458. // CCocoaThreadMsg is what the REAL code (C++ code) deals with when it passes
  459. // messages between the Valve thread and the Cocoa thread.
  460. //
  461. // We use NSObject::performSelectorInMainThread to pass the messages back
  462. // and forth, so we have to stuff the CCocoaThreadMsg into an Objective C++ object
  463. // called CCocoaThreadMsgContainer
  464. //
  465. // ------------------------------------------------------------------------------------ //
  466. class CCocoaThreadMsg
  467. {
  468. public:
  469. virtual ~CCocoaThreadMsg() {}
  470. };
  471. class CCocoaThreadMsg_CreateWindow : public CCocoaThreadMsg
  472. {
  473. public:
  474. char m_Title[256];
  475. bool m_bWindowed;
  476. int m_nWidth;
  477. int m_nHeight;
  478. };
  479. @interface CCocoaThreadMsgContainer : NSObject
  480. {
  481. @public
  482. CCocoaThreadMsg *m_pMessage;
  483. }
  484. @end
  485. @implementation CCocoaThreadMsgContainer
  486. @end
  487. // ------------------------------------------------------------------------------------ //
  488. // CocoaBridge Objective-C class. This is the bridge between NSApplication and
  489. // the Valve code in CCocoaMgr. It just forwards Cocoa messages to CCocoaMgr.
  490. // ------------------------------------------------------------------------------------ //
  491. @interface CocoaBridge : NSObject<NSApplicationDelegate>
  492. {
  493. @public
  494. CCocoaMgr *m_pCocoaMgr;
  495. }
  496. /*
  497. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
  498. - (void)applicationWillTerminate:(NSNotification *)aNotification;
  499. - (void)ProcessMessage:(id)pObj;
  500. - (void)applicationDidBecomeActive:(NSNotification *)aNotification;
  501. - (void)applicationDidResignActive:(NSNotification *)aNotification;
  502. - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication
  503. hasVisibleWindows:(BOOL)flag;
  504. -(void) populateApplicationMenu:(NSMenu *)aMenu;
  505. -(void) populateWindowMenu:(NSMenu *)aMenu;
  506. -(void) applicationWillFinishLaunching:(NSNotification *)aNotification;
  507. */
  508. @end
  509. @implementation CocoaBridge
  510. #pragma mark NSApplication delegate
  511. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
  512. {
  513. CCocoaEvent theEvent;
  514. theEvent.m_EventType = CocoaEvent_AppQuit;
  515. m_pCocoaMgr->PostEvent( theEvent );
  516. return NSTerminateCancel;
  517. }
  518. - (void)applicationWillTerminate:(NSNotification *)aNotification
  519. {
  520. m_pCocoaMgr->applicationWillTerminate();
  521. }
  522. - (void)ProcessMessage:(id)pObj
  523. {
  524. CCocoaThreadMsgContainer *pDispatch = (CCocoaThreadMsgContainer*)pObj;
  525. m_pCocoaMgr->ProcessMessageInCocoaThread( pDispatch->m_pMessage );
  526. delete pDispatch->m_pMessage;
  527. [pDispatch release];
  528. }
  529. - (void)applicationDidBecomeActive:(NSNotification *)aNotification
  530. {
  531. CCocoaEvent theEvent;
  532. theEvent.m_EventType = CocoaEvent_AppActivate;
  533. theEvent.m_ModifierKeyMask = 1;
  534. m_pCocoaMgr->PostEvent( theEvent );
  535. }
  536. - (void)applicationDidResignActive:(NSNotification *)aNotification
  537. {
  538. CCocoaEvent theEvent;
  539. theEvent.m_EventType = CocoaEvent_AppActivate;
  540. theEvent.m_ModifierKeyMask = 0;
  541. m_pCocoaMgr->PostEvent( theEvent );
  542. }
  543. - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication
  544. hasVisibleWindows:(BOOL)flag
  545. {
  546. m_pCocoaMgr->ShowMainWindow();
  547. return YES;
  548. }
  549. -(void) populateApplicationMenu:(NSMenu *)aMenu
  550. {
  551. NSString * applicationName = NSLocalizedString(@"Source", nil);
  552. NSMenuItem * menuItem;
  553. menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"About", nil), applicationName]
  554. action:@selector(openAboutDialog:)
  555. keyEquivalent:@""];
  556. [menuItem setTarget:self];
  557. [aMenu addItem:[NSMenuItem separatorItem]];
  558. menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Preferences...", nil)
  559. action:@selector(openPreferencesDialog:)
  560. keyEquivalent:@","];
  561. [menuItem setTarget:self];
  562. [aMenu addItem:[NSMenuItem separatorItem]];
  563. menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Hide", nil), applicationName]
  564. action:@selector(hide:)
  565. keyEquivalent:@"h"];
  566. [menuItem setTarget:NSApp];
  567. menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Hide Others", nil)
  568. action:@selector(hideOtherApplications:)
  569. keyEquivalent:@"h"];
  570. [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask];
  571. [menuItem setTarget:NSApp];
  572. menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Show All", nil)
  573. action:@selector(unhideAllApplications:)
  574. keyEquivalent:@""];
  575. [menuItem setTarget:NSApp];
  576. [aMenu addItem:[NSMenuItem separatorItem]];
  577. menuItem = [aMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Quit", nil), applicationName]
  578. action:@selector(terminate:)
  579. keyEquivalent:@"q"];
  580. [menuItem setTarget:NSApp];
  581. }
  582. -(void) populateWindowMenu:(NSMenu *)aMenu
  583. {
  584. NSMenuItem * menuItem;
  585. menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Minimize", nil)
  586. action:@selector(performMinimize:)
  587. keyEquivalent:@"m"];
  588. menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Zoom", nil)
  589. action:@selector(performZoom:)
  590. keyEquivalent:@""];
  591. [aMenu addItem:[NSMenuItem separatorItem]];
  592. menuItem = [aMenu addItemWithTitle:NSLocalizedString(@"Bring All to Front", nil)
  593. action:@selector(arrangeInFront:)
  594. keyEquivalent:@""];
  595. }
  596. //-----------------------------------------------------------------------------
  597. // Purpose: called on startup, populates the OS level menus
  598. //-----------------------------------------------------------------------------
  599. -(void) applicationWillFinishLaunching:(NSNotification *)aNotification
  600. {
  601. // NSApplication might not have created an autorelease pool yet, so create our own.
  602. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  603. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  604. NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"AppleMomentumScrollSupported"];
  605. [defaults registerDefaults:appDefaults];
  606. NSMenu * mainMenu = [[NSMenu alloc] initWithTitle:@"MainMenu"];
  607. NSMenuItem * menuItem;
  608. NSMenu * submenu;
  609. // The titles of the menu items are for identification purposes only and shouldn't be localized.
  610. // The strings in the menu bar come from the submenu titles,
  611. // except for the application menu, whose title is ignored at runtime.
  612. menuItem = [mainMenu addItemWithTitle:@"Apple" action:NULL keyEquivalent:@""];
  613. submenu = [[NSMenu alloc] initWithTitle:@"Apple"];
  614. [NSApp performSelector:@selector(setAppleMenu:) withObject:submenu];
  615. [self populateApplicationMenu:submenu];
  616. [mainMenu setSubmenu:submenu forItem:menuItem];
  617. menuItem = [mainMenu addItemWithTitle:@"Window" action:NULL keyEquivalent:@""];
  618. submenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Window", @"The Window menu")];
  619. [self populateWindowMenu:submenu];
  620. [mainMenu setSubmenu:submenu forItem:menuItem];
  621. [NSApp setWindowsMenu:submenu];
  622. /*
  623. menuItem = [mainMenu addItemWithTitle:@"Help" action:NULL keyEquivalent:@""];
  624. submenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Help", @"The Help menu")];
  625. [self populateHelpMenu:submenu];
  626. [mainMenu setSubmenu:submenu forItem:menuItem];
  627. */
  628. [NSApp setMainMenu:mainMenu];
  629. /* NSAppleEventManager *appleEventManager = [NSAppleEventManager
  630. sharedAppleEventManager];
  631. // Get URL Apple Event ('GURL') is part of the internet AE suite not the standard AE suite and
  632. // it isn't currently supported directly via a application delegate method so we have to register
  633. // an AE event handler for it.
  634. [appleEventManager setEventHandler:self
  635. andSelector:@selector(handleGetURLEvent:withReplyEvent:)
  636. forEventClass:'GURL'
  637. andEventID:'GURL'];
  638. */
  639. [pool release];
  640. }
  641. @end
  642. @interface NSValveApplication : NSApplication
  643. {
  644. }
  645. - (void)sendEvent:(NSEvent *)anEvent;
  646. @end
  647. @implementation NSValveApplication
  648. - (void)sendEvent:(NSEvent *)anEvent
  649. {
  650. //This works around an AppKit bug, where key up events while holding
  651. //down the command key don't get sent to the key window.
  652. if( ([anEvent modifierFlags] & NSCommandKeyMask))
  653. {
  654. NSEventType type = [anEvent type];
  655. //printf( "Got event %d (%d)\n", [anEvent type], NSKeyUp );
  656. if ( type == NSKeyUp || type == NSKeyDown
  657. || type == NSLeftMouseDown || type == NSLeftMouseUp
  658. || type == NSRightMouseDown || type == NSRightMouseUp
  659. || type == NSOtherMouseDown || type == NSOtherMouseUp
  660. || type == NSLeftMouseDragged || type == NSRightMouseDragged
  661. || type == NSFlagsChanged || type == NSMouseMoved || type == NSScrollWheel )
  662. {
  663. if ( type == NSKeyUp || type == NSKeyDown )
  664. {
  665. // tell both the high level and us for CMD-Q,W,H,M
  666. int keyCode = [anEvent keyCode];
  667. if ( keyCode == 12 || keyCode == 13 || keyCode == 4 || keyCode == 46 )
  668. [super sendEvent:anEvent];
  669. }
  670. //printf( "%i\n", keyCode );
  671. [[self keyWindow] sendEvent:anEvent];
  672. }
  673. else
  674. [super sendEvent:anEvent];
  675. }
  676. else
  677. {
  678. [super sendEvent:anEvent];
  679. }
  680. }
  681. @end
  682. @interface CocoaViewDelegate : NSObject<NSWindowDelegate>
  683. {
  684. }
  685. @end
  686. @implementation CocoaViewDelegate
  687. - (void)windowDidDeminiaturize:(NSNotification *)notification
  688. {
  689. CGDisplayHideCursor(kCGDirectMainDisplay);
  690. }
  691. @end
  692. // ------------------------------------------------------------------------------------ //
  693. // Our NSView bridge.
  694. // ------------------------------------------------------------------------------------ //
  695. @interface CocoaViewBridge : NSOpenGLView
  696. {
  697. @public
  698. CCocoaMgr *m_pCocoaMgr;
  699. }
  700. @end
  701. @implementation CocoaViewBridge
  702. - (void)drawRect:(NSRect)rect
  703. {
  704. // letting this method do anything was having evil effects on clipping (black portal bug).
  705. // we need a better solution though, or we will get garbage in the window at launch
  706. return;
  707. }
  708. - (BOOL)acceptsFirstResponder
  709. {
  710. return YES;
  711. }
  712. - (void)mouseDown:(NSEvent*)pEvent
  713. {
  714. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  715. CCocoaEvent theEvent;
  716. theEvent.m_EventType = CocoaEvent_MouseButtonDown;
  717. theEvent.m_MousePos[0] = (int)pt.x;
  718. theEvent.m_MousePos[1] = (int)pt.y;
  719. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  720. theEvent.m_nMouseClickCount = [pEvent clickCount];
  721. if ( [pEvent modifierFlags] & NSCommandKeyMask ) // make ctrl-click be a right click
  722. {
  723. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  724. theEvent.m_MouseButtonFlags &= ~0x1; // turn off left button press
  725. theEvent.m_MouseButtonFlags |= 0x2; // and press right instead
  726. theEvent.m_MouseButton = COCOABUTTON_RIGHT;
  727. }
  728. else
  729. {
  730. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  731. theEvent.m_MouseButton = COCOABUTTON_LEFT;
  732. }
  733. m_pCocoaMgr->PostEvent( theEvent );
  734. }
  735. - (void)rightMouseDown:(NSEvent*)pEvent
  736. {
  737. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  738. CCocoaEvent theEvent;
  739. theEvent.m_EventType = CocoaEvent_MouseButtonDown;
  740. theEvent.m_MousePos[0] = (int)pt.x;
  741. theEvent.m_MousePos[1] = (int)pt.y;
  742. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  743. theEvent.m_nMouseClickCount = [pEvent clickCount];
  744. theEvent.m_MouseButton = COCOABUTTON_RIGHT;
  745. m_pCocoaMgr->PostEvent( theEvent );
  746. }
  747. - (void)otherMouseDown:(NSEvent*)pEvent
  748. {
  749. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  750. CCocoaEvent theEvent;
  751. theEvent.m_EventType = CocoaEvent_MouseButtonDown;
  752. theEvent.m_MousePos[0] = (int)pt.x;
  753. theEvent.m_MousePos[1] = (int)pt.y;
  754. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  755. theEvent.m_nMouseClickCount = [pEvent clickCount];
  756. switch( [pEvent buttonNumber] )
  757. {
  758. case 2:
  759. theEvent.m_MouseButton = COCOABUTTON_MIDDLE;
  760. break;
  761. case 3:
  762. theEvent.m_MouseButton = COCOABUTTON_4;
  763. break;
  764. case 4:
  765. theEvent.m_MouseButton = COCOABUTTON_5;
  766. break;
  767. }
  768. m_pCocoaMgr->PostEvent( theEvent );
  769. }
  770. - (void)mouseMoved:(NSEvent*)pEvent
  771. {
  772. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  773. CCocoaEvent theEvent;
  774. theEvent.m_EventType = CocoaEvent_MouseMove;
  775. theEvent.m_MousePos[0] = (int)pt.x;
  776. theEvent.m_MousePos[1] = (int)pt.y;
  777. NSPoint orig = [NSEvent mouseLocation];
  778. //printf( "MouseMove: %0.1f,%0.1f %0.1f,%0.1f\n", pt.x, pt.y, orig.x, orig.y );
  779. m_pCocoaMgr->PostEvent( theEvent );
  780. int32 deltaX, deltaY;
  781. CGGetLastMouseDelta( &deltaX, &deltaY );
  782. m_pCocoaMgr->AccumlateMouseDelta( deltaX, deltaY );
  783. }
  784. - (void)mouseDragged:(NSEvent*)pEvent
  785. {
  786. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  787. int32 deltaX, deltaY;
  788. CGGetLastMouseDelta( &deltaX, &deltaY );
  789. m_pCocoaMgr->AccumlateMouseDelta( deltaX, deltaY );
  790. if ( !CGCursorIsVisible() )
  791. {
  792. pt.x += deltaX;
  793. pt.y += deltaY;
  794. }
  795. CCocoaEvent theEvent;
  796. theEvent.m_EventType = CocoaEvent_MouseMove;
  797. theEvent.m_MousePos[0] = (int)pt.x;
  798. theEvent.m_MousePos[1] = (int)pt.y;
  799. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  800. m_pCocoaMgr->PostEvent( theEvent );
  801. }
  802. - (void)rightMouseDragged:(NSEvent*)pEvent
  803. {
  804. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  805. int32 deltaX, deltaY;
  806. CGGetLastMouseDelta( &deltaX, &deltaY );
  807. m_pCocoaMgr->AccumlateMouseDelta( deltaX, deltaY );
  808. if ( !CGCursorIsVisible() )
  809. {
  810. pt.x += deltaX;
  811. pt.y += deltaY;
  812. }
  813. CCocoaEvent theEvent;
  814. theEvent.m_EventType = CocoaEvent_MouseMove;
  815. theEvent.m_MousePos[0] = (int)pt.x;
  816. theEvent.m_MousePos[1] = (int)pt.y;
  817. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  818. m_pCocoaMgr->PostEvent( theEvent );
  819. }
  820. - (void)otherMouseDragged:(NSEvent*)pEvent
  821. {
  822. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  823. int32 deltaX, deltaY;
  824. CGGetLastMouseDelta( &deltaX, &deltaY );
  825. m_pCocoaMgr->AccumlateMouseDelta( deltaX, deltaY );
  826. if ( !CGCursorIsVisible() )
  827. {
  828. pt.x += deltaX;
  829. pt.y += deltaY;
  830. }
  831. CCocoaEvent theEvent;
  832. theEvent.m_EventType = CocoaEvent_MouseMove;
  833. theEvent.m_MousePos[0] = (int)pt.x;
  834. theEvent.m_MousePos[1] = (int)pt.y;
  835. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  836. m_pCocoaMgr->PostEvent( theEvent );
  837. }
  838. - (void)mouseUp:(NSEvent*)pEvent
  839. {
  840. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  841. CCocoaEvent theEvent;
  842. theEvent.m_EventType = CocoaEvent_MouseButtonUp;
  843. theEvent.m_MousePos[0] = (int)pt.x;
  844. theEvent.m_MousePos[1] = (int)pt.y;
  845. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  846. m_pCocoaMgr->PostEvent( theEvent );
  847. }
  848. - (void)rightMouseUp:(NSEvent*)pEvent
  849. {
  850. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  851. CCocoaEvent theEvent;
  852. theEvent.m_EventType = CocoaEvent_MouseButtonUp;
  853. theEvent.m_MousePos[0] = (int)pt.x;
  854. theEvent.m_MousePos[1] = (int)pt.y;
  855. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  856. m_pCocoaMgr->PostEvent( theEvent );
  857. }
  858. - (void)otherMouseUp:(NSEvent*)pEvent
  859. {
  860. NSPoint pt = m_pCocoaMgr->ScreenCoordsToSourceWindowCoords( self, [NSEvent mouseLocation] );
  861. CCocoaEvent theEvent;
  862. theEvent.m_EventType = CocoaEvent_MouseButtonUp;
  863. theEvent.m_MousePos[0] = (int)pt.x;
  864. theEvent.m_MousePos[1] = (int)pt.y;
  865. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  866. m_pCocoaMgr->PostEvent( theEvent );
  867. }
  868. - (void)keyDown:(NSEvent *)pEvent
  869. {
  870. // Store the event in our input event queue.
  871. CCocoaEvent theEvent;
  872. theEvent.m_EventType = CocoaEvent_KeyDown;
  873. theEvent.m_VirtualKeyCode = pEvent.keyCode;
  874. theEvent.m_UnicodeKey = 0;
  875. if ( [pEvent.characters length] > 0 )
  876. theEvent.m_UnicodeKey = [pEvent.characters characterAtIndex:0];
  877. theEvent.m_UnicodeKeyUnmodified = 0;
  878. if ( [pEvent.charactersIgnoringModifiers length] > 0 )
  879. theEvent.m_UnicodeKeyUnmodified = [pEvent.charactersIgnoringModifiers characterAtIndex:0];
  880. if ( theEvent.m_UnicodeKey & 0x8000 ) // apple set the high bit for "special" characters like arrow keys, so ignore them
  881. {
  882. theEvent.m_UnicodeKey = theEvent.m_UnicodeKeyUnmodified = 0;
  883. }
  884. theEvent.m_ModifierKeyMask = 0;
  885. // pick apart modifier key mask
  886. uint modifiers = [pEvent modifierFlags];
  887. if (modifiers & NSAlphaShiftKeyMask)
  888. {
  889. theEvent.m_ModifierKeyMask |= (1<<eCapsLockKey);
  890. }
  891. if (modifiers & NSShiftKeyMask)
  892. {
  893. theEvent.m_ModifierKeyMask |= (1<<eShiftKey);
  894. }
  895. if (modifiers & NSAlternateKeyMask)
  896. {
  897. theEvent.m_ModifierKeyMask |= (1<<eAltKey);
  898. }
  899. if (modifiers & NSCommandKeyMask)
  900. {
  901. theEvent.m_ModifierKeyMask |= (1<<eCommandKey);
  902. }
  903. if (modifiers & NSControlKeyMask)
  904. {
  905. theEvent.m_ModifierKeyMask |= (1<<eControlKey);
  906. }
  907. // make a decision about this event - does it go in the normal evt queue or into the debug queue.
  908. bool debug = m_pCocoaMgr->IsDebugEvent( theEvent );
  909. //if ( [pEvent isARepeat] && (!debug) ) // dump non debug key repeats
  910. // return;
  911. m_pCocoaMgr->PostEvent( theEvent, debug );
  912. // This was used to build a table of virtual key codes to ButtonCode_t's.
  913. //char str[512];
  914. //V_strncpy( str, [pEvent.characters UTF8String], sizeof( str ) );
  915. //V_strupr( str );
  916. //Msg( "{0x%x, KEY_%s},\n", pEvent.keyCode, str );
  917. }
  918. void CheckModifierDiff( UInt32 diff, UInt32 mask, UInt32 old, ButtonCode_t key, CCocoaMgr *m_pCocoaMgr )
  919. {
  920. if( diff & mask )
  921. {
  922. // Store the event in our input event queue.
  923. CCocoaEvent theEvent;
  924. theEvent.m_VirtualKeyCode = -1 * key; // use a negative code value so the input system can translate it directly to a buttoncode
  925. theEvent.m_UnicodeKey = theEvent.m_UnicodeKeyUnmodified = 0;
  926. theEvent.m_ModifierKeyMask = 0;
  927. if ( old & mask ) // was pressed, now isnt
  928. {
  929. theEvent.m_EventType = CocoaEvent_KeyUp;
  930. }
  931. else
  932. {
  933. theEvent.m_EventType = CocoaEvent_KeyDown;
  934. }
  935. // make a decision about this event - does it go in the normal evt queue or into the debug queue.
  936. bool debug = m_pCocoaMgr->IsDebugEvent( theEvent );
  937. m_pCocoaMgr->PostEvent( theEvent, debug );
  938. }
  939. }
  940. - (void)flagsChanged:(NSEvent *)pEvent
  941. {
  942. // pick apart modifier key mask
  943. uint modifierCode = [pEvent modifierFlags];
  944. static uint s_lastModifierCode = 0;
  945. UInt32 diff = s_lastModifierCode ^ modifierCode;
  946. CheckModifierDiff( diff, NSAlphaShiftKeyMask, s_lastModifierCode, KEY_CAPSLOCK, m_pCocoaMgr );
  947. CheckModifierDiff( diff, NSShiftKeyMask, s_lastModifierCode, KEY_LSHIFT, m_pCocoaMgr );
  948. CheckModifierDiff( diff, NSAlternateKeyMask, s_lastModifierCode, KEY_LALT, m_pCocoaMgr );
  949. CheckModifierDiff( diff, NSCommandKeyMask, s_lastModifierCode, KEY_LWIN, m_pCocoaMgr );
  950. CheckModifierDiff( diff, NSControlKeyMask, s_lastModifierCode, KEY_LCONTROL, m_pCocoaMgr );
  951. s_lastModifierCode = modifierCode;
  952. }
  953. - (void)keyUp:(NSEvent *)pEvent
  954. {
  955. CCocoaEvent theEvent;
  956. theEvent.m_EventType = CocoaEvent_KeyUp;
  957. theEvent.m_VirtualKeyCode = pEvent.keyCode;
  958. theEvent.m_UnicodeKey = 0;
  959. if ( [pEvent.characters length] > 0 )
  960. theEvent.m_UnicodeKey = [pEvent.characters characterAtIndex:0];
  961. theEvent.m_UnicodeKeyUnmodified = 0;
  962. if ( [pEvent.charactersIgnoringModifiers length] > 0 )
  963. theEvent.m_UnicodeKeyUnmodified = [pEvent.charactersIgnoringModifiers characterAtIndex:0];
  964. theEvent.m_ModifierKeyMask = 0;
  965. // pick apart modifier key mask
  966. uint modifiers = [pEvent modifierFlags];
  967. if (modifiers & NSAlphaShiftKeyMask)
  968. {
  969. theEvent.m_ModifierKeyMask |= (1<<eCapsLockKey);
  970. }
  971. if (modifiers & NSShiftKeyMask)
  972. {
  973. theEvent.m_ModifierKeyMask |= (1<<eShiftKey);
  974. }
  975. if (modifiers & NSAlternateKeyMask)
  976. {
  977. theEvent.m_ModifierKeyMask |= (1<<eAltKey);
  978. }
  979. if (modifiers & NSCommandKeyMask)
  980. {
  981. theEvent.m_ModifierKeyMask |= (1<<eCommandKey);
  982. }
  983. if (modifiers & NSControlKeyMask)
  984. {
  985. theEvent.m_ModifierKeyMask |= (1<<eControlKey);
  986. }
  987. m_pCocoaMgr->PostEvent( theEvent );
  988. }
  989. - (BOOL)performKeyEquivalent:(NSEvent *)theEvent
  990. {
  991. if ( theEvent.keyCode == 48 )
  992. {
  993. if ([theEvent type] == NSKeyDown )
  994. {
  995. [self keyDown: theEvent ];
  996. }
  997. else
  998. {
  999. [self keyUp: theEvent ];
  1000. }
  1001. }
  1002. return [super performKeyEquivalent:theEvent];
  1003. }
  1004. - (void)scrollWheel:(NSEvent *)pEvent
  1005. {
  1006. CCocoaEvent theEvent;
  1007. theEvent.m_EventType = CocoaEvent_MouseScroll;
  1008. theEvent.m_MousePos[0] = [pEvent deltaX]*100;
  1009. theEvent.m_MousePos[1] = [pEvent deltaY]*100;
  1010. theEvent.m_MouseButtonFlags = GetCurrentMouseButtonState( );
  1011. m_pCocoaMgr->PostEvent( theEvent );
  1012. }
  1013. @end
  1014. // ------------------------------------------------------------------------------------ //
  1015. // CCocoaMgr implementation.
  1016. // ------------------------------------------------------------------------------------ //
  1017. CCocoaMgr::CCocoaMgr()
  1018. {
  1019. m_application = NULL;
  1020. m_window = NULL;
  1021. m_view = NULL;
  1022. // You cannot make the display DB here.. because it will want to query some CLI args...
  1023. // and this constructor happens too early in the program lifetime.
  1024. // so leave it NULL, and teach the GetDisplayDB function to do a one-time init.
  1025. m_displayDB = NULL;
  1026. m_renderedWidth = m_rendererHeight = 0;
  1027. m_chosenRendererIndex = 0; //FIXME
  1028. memset( m_pixelFormatAttribs, 0, sizeof(m_pixelFormatAttribs) );
  1029. m_pixelFormatAttribCount = 0;
  1030. m_nRunLoopThreadID = (ThreadId_t)-1;
  1031. m_pCocoaBridge = NULL;
  1032. m_MouseDeltaX = 0;
  1033. m_MouseDeltaY = 0;
  1034. m_bIgnoreNextMouseDelta = false;
  1035. m_flPrevGLSwapWindowTime = 0.0f;
  1036. int32 deltaX, deltaY;
  1037. CGGetLastMouseDelta( &deltaX, &deltaY );
  1038. memset( &m_lastShownPixels, 0, sizeof(m_lastShownPixels) );
  1039. // set some silly rectangles for initial win frame and fs frame
  1040. m_winFrame.origin.x = 50.0f;
  1041. m_winFrame.origin.y = 50.0f;
  1042. m_winFrame.size.width = 640.0f;
  1043. m_winFrame.size.height = 480.0f;
  1044. m_fsFrame = [[NSScreen mainScreen] frame];
  1045. m_showPixelsCtx = NULL;
  1046. m_lastKnownSwapInterval = -1;
  1047. m_lastKnownSwapLimit = -1;
  1048. m_frontPushCounter = 3;
  1049. m_windowTitle = "";
  1050. }
  1051. void CCocoaMgr::AccumlateMouseDelta( int32 &x, int32 &y )
  1052. {
  1053. if ( !m_bIgnoreNextMouseDelta )
  1054. {
  1055. m_MouseDeltaX += x;
  1056. m_MouseDeltaY += y;
  1057. }
  1058. m_bIgnoreNextMouseDelta = false;
  1059. }
  1060. void CCocoaMgr::GetMouseDelta( int &x, int &y, bool bIgnoreNextDelta )
  1061. {
  1062. if ( bIgnoreNextDelta )
  1063. m_bIgnoreNextMouseDelta = bIgnoreNextDelta;
  1064. x = m_MouseDeltaX;
  1065. y = m_MouseDeltaY;
  1066. m_MouseDeltaX = m_MouseDeltaY = 0;
  1067. }
  1068. void CCocoaMgr::ShowMainWindow()
  1069. {
  1070. [m_window makeKeyAndOrderFront:nil];
  1071. }
  1072. void CCocoaMgr::RenderedSize( uint& width, uint& height, bool set )
  1073. {
  1074. if (set)
  1075. {
  1076. m_renderedWidth = width;
  1077. m_rendererHeight = height; // latched from NotifyRenderedSize
  1078. }
  1079. else
  1080. {
  1081. width = m_renderedWidth;
  1082. height = m_rendererHeight;
  1083. }
  1084. }
  1085. void CCocoaMgr::DisplayedSize( uint &width, uint &height )
  1086. {
  1087. if (m_view)
  1088. {
  1089. NSRect rect = [m_view frame];
  1090. width = rect.size.width;
  1091. height = rect.size.height;
  1092. }
  1093. else
  1094. {
  1095. width = height = 1;
  1096. }
  1097. }
  1098. void CCocoaMgr::GetDesiredPixelFormatAttribsAndRendererInfo( uint **ptrOut, uint *countOut, GLMRendererInfoFields *rendInfoOut )
  1099. {
  1100. Assert( m_pixelFormatAttribCount > 0 );
  1101. if (ptrOut) *ptrOut = m_pixelFormatAttribs;
  1102. if (countOut) *countOut = m_pixelFormatAttribCount;
  1103. if (rendInfoOut)
  1104. {
  1105. GLMDisplayDB *db = GetDisplayDB();
  1106. *rendInfoOut = ((*db->m_renderers)[ m_chosenRendererIndex ])->m_info;
  1107. }
  1108. }
  1109. GLMDisplayDB *CCocoaMgr::GetDisplayDB( void )
  1110. {
  1111. if (!m_displayDB)
  1112. {
  1113. m_displayDB = new GLMDisplayDB(); // creating the DB object does not do much other than init it to a good state.
  1114. m_displayDB->Populate(); // populate the tree
  1115. // side effect: we fill in m_leopard and m_force_vsync..
  1116. {
  1117. GLMRendererInfoFields info;
  1118. m_displayDB->GetRendererInfo( 0, &info );
  1119. m_leopard = (info.m_osComboVersion < 0x000A0600);
  1120. m_force_vsync = info.m_badDriver1064NV; // just force it if it's the bum NV driver
  1121. }
  1122. }
  1123. return m_displayDB;
  1124. }
  1125. void CCocoaMgr::ProcessMessageInCocoaThread( CCocoaThreadMsg *pInMessage )
  1126. {
  1127. CCocoaThreadMsg_CreateWindow *pMsg1 = dynamic_cast< CCocoaThreadMsg_CreateWindow* >( pInMessage );
  1128. if ( pMsg1 )
  1129. {
  1130. // we could improve this to let the caller pass in a specific frame instead of just centering on the main screen
  1131. NSRect screenFrame = [[NSScreen mainScreen] frame];
  1132. int sw = screenFrame.size.width;
  1133. int sh = screenFrame.size.height;
  1134. NSRect newWinFrame;
  1135. if ( pMsg1->m_bWindowed )
  1136. {
  1137. newWinFrame.origin.x = ((sw - pMsg1->m_nWidth) / 2); // int math to truncate prior to assignment
  1138. newWinFrame.origin.y = ((sh - pMsg1->m_nHeight) / 2); // int math to truncate prior to assignment
  1139. newWinFrame.size.width = pMsg1->m_nWidth;
  1140. newWinFrame.size.height = pMsg1->m_nHeight;
  1141. }
  1142. else
  1143. {
  1144. newWinFrame = screenFrame;
  1145. }
  1146. InternalCreateWindow( pMsg1->m_Title, pMsg1->m_bWindowed, &newWinFrame );
  1147. }
  1148. }
  1149. void CCocoaMgr::SendMessageToCocoaThread( CCocoaThreadMsg *pMessage, bool bWaitUntilDone )
  1150. {
  1151. if ( ThreadGetCurrentId() == m_nRunLoopThreadID )
  1152. {
  1153. // We're already in the Cocoa thread. Process it immediately.
  1154. ProcessMessageInCocoaThread( pMessage );
  1155. delete pMessage;
  1156. }
  1157. else
  1158. {
  1159. Assert( !"We shouldn't be running in a thread anymore" );
  1160. CCocoaThreadMsgContainer *pContainer = [[CCocoaThreadMsgContainer alloc] init];
  1161. pContainer->m_pMessage = pMessage;
  1162. [m_pCocoaBridge performSelectorOnMainThread:@selector(ProcessMessage:) withObject:pContainer waitUntilDone:bWaitUntilDone];
  1163. }
  1164. }
  1165. void CCocoaMgr::PostEvent( const CCocoaEvent &theEvent, bool debugEvent )
  1166. {
  1167. m_CocoaEventsMutex.Lock();
  1168. CUtlLinkedList<CCocoaEvent,int> &queue = debugEvent ? m_CocoaEvents : m_DebugEvents;
  1169. queue.AddToTail( theEvent );
  1170. m_CocoaEventsMutex.Unlock();
  1171. }
  1172. bool CCocoaMgr::CreateApplicationObject() // return false if this is already done (i.e. a mod is trying to kick things off again)
  1173. {
  1174. //Assert( !m_application );
  1175. if (!m_application)
  1176. {
  1177. // just do this stuff once
  1178. // tell OSX we are actually an app
  1179. ProcessSerialNumber psn = { 0, kCurrentProcess };
  1180. TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  1181. SetFrontProcess(&psn);
  1182. m_application = [NSValveApplication sharedApplication];
  1183. return true;
  1184. }
  1185. else
  1186. {
  1187. return false;
  1188. }
  1189. }
  1190. void CCocoaMgr::DestroyGameWindow()
  1191. {
  1192. /*
  1193. NOP baby - you get one window and like it.
  1194. [[m_window delegate] release];
  1195. [m_window close];
  1196. m_window = NULL;
  1197. m_view = NULL;
  1198. */
  1199. }
  1200. bool CCocoaMgr::CreateGameWindow( const char *pTitle, bool bWindowed, int width, int height )
  1201. {
  1202. // if m_window is already set, we skip this.
  1203. if (!m_window)
  1204. {
  1205. // Most things with Cocoa objects have to be done in the Cocoa thread
  1206. CCocoaThreadMsg_CreateWindow *pMessage = new CCocoaThreadMsg_CreateWindow;
  1207. V_strncpy( pMessage->m_Title, pTitle, sizeof( pMessage->m_Title ) );
  1208. pMessage->m_bWindowed = bWindowed;
  1209. pMessage->m_nWidth = width;
  1210. pMessage->m_nHeight = height;
  1211. SendMessageToCocoaThread( pMessage, true );
  1212. }
  1213. return true;
  1214. }
  1215. bool CCocoaMgr::InternalCreateWindow( const char *pTitle, bool bWindowed, NSRect *frame )
  1216. {
  1217. //stash the window title..
  1218. if (pTitle)
  1219. {
  1220. m_windowTitle = strdup( pTitle );
  1221. }
  1222. // Setup an autorelease pool.
  1223. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1224. //-----------------------------------------------------------------------------------------
  1225. // pick our favorite renderer (presently we just grab 0 in renderer table)
  1226. //FIXME allow for other renderers to be selected somehow
  1227. GLMRendererInfoFields rendererInfo;
  1228. GetDisplayDB()->GetRendererInfo( m_chosenRendererIndex, &rendererInfo );
  1229. //-----------------------------------------------------------------------------------------
  1230. //- enforce minimum system requirements : OS X 10.7, Lion, and no GMA950, X3100, ATI X1600/X1900, or NV G7x.
  1231. if (!CommandLine()->FindParm("-glmnosystemcheck")) // escape hatch
  1232. {
  1233. if ( rendererInfo.m_osComboVersion < 0x0A0700 )
  1234. {
  1235. Error( "This game requires OS X version 10.7 or higher" );
  1236. exit(1);
  1237. }
  1238. // forbidden chipsets
  1239. if ( rendererInfo.m_atiR5xx || rendererInfo.m_intel95x || rendererInfo.m_intel3100 || rendererInfo.m_nvG7x )
  1240. {
  1241. Error( "This game does not support this type of graphics processor" );
  1242. exit(1);
  1243. }
  1244. }
  1245. //-----------------------------------------------------------------------------------------
  1246. // write the preferred attribs
  1247. uint *attCursor = m_pixelFormatAttribs;
  1248. *attCursor++ = kCGLPFADoubleBuffer;
  1249. *attCursor++ = kCGLPFANoRecovery;
  1250. *attCursor++ = kCGLPFAAccelerated;
  1251. *attCursor++ = kCGLPFADepthSize;
  1252. *attCursor++ = 0; // no explicit depth buffer is needed since FBO RT's are made for that
  1253. *attCursor++ = kCGLPFAColorSize;
  1254. *attCursor++ = 32;
  1255. *attCursor++ = kCGLPFARendererID;
  1256. *attCursor++ = rendererInfo.m_rendererID;
  1257. if (CommandLine()->FindParm("-glmnobackingstore"))
  1258. {
  1259. *attCursor++ = kCGLPFABackingStore;
  1260. *attCursor++ = 0; // "NO BACKING STORE PLEASE" so swaps are possible
  1261. }
  1262. *attCursor++ = 0;
  1263. // log attrib count
  1264. m_pixelFormatAttribCount = attCursor - &m_pixelFormatAttribs[0];
  1265. //-----------------------------------------------------------------------------------------
  1266. // Create the window.
  1267. int style = bWindowed ? s_windowedStyleMask : s_fullscreenStyleMask;
  1268. AppWindow *newWin = [[AppWindow alloc] initWithContentRect:*frame styleMask:style backing:NSBackingStoreBuffered defer:YES];
  1269. [newWin retain];
  1270. CocoaViewDelegate *delegate = [[CocoaViewDelegate alloc] init];
  1271. [delegate retain];
  1272. [newWin setDelegate: delegate ];
  1273. // Set the title.
  1274. // if pTitle is NULL, use previously stashed title..
  1275. if (pTitle)
  1276. {
  1277. [newWin setTitle:[NSString stringWithUTF8String:pTitle]];
  1278. }
  1279. else
  1280. {
  1281. [newWin setTitle:[NSString stringWithUTF8String:m_windowTitle]];
  1282. }
  1283. //-----------------------------------------------------------------------------------------
  1284. // Set the content view to be our own NSOpenGLView, using the preferred attribute list.
  1285. // **unless** we have a gl view from an old window, in which case we slot that one in instead.
  1286. if (!m_view) // one time only...
  1287. {
  1288. CGLPixelFormatAttribute *selAttribs = NULL;
  1289. uint selWords = 0;
  1290. this->GetDesiredPixelFormatAttribsAndRendererInfo( (uint**)&selAttribs, &selWords, NULL );
  1291. NSOpenGLPixelFormat* nsglFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*)selAttribs];
  1292. CocoaViewBridge *newView = [[CocoaViewBridge alloc] initWithFrame:[newWin frame] pixelFormat:nsglFormat ];
  1293. [ newView retain ];
  1294. newView->m_pCocoaMgr = this;
  1295. m_view = newView;
  1296. }
  1297. if (1)
  1298. {
  1299. [[m_view openGLContext] makeCurrentContext];
  1300. NSRect rect = [m_view frame];
  1301. glViewport(0, 0, (GLsizei) rect.size.width, (GLsizei) rect.size.height);
  1302. glScissor( 0,0, (GLsizei) rect.size.width, (GLsizei) rect.size.height );
  1303. }
  1304. [newWin setContentView:m_view];
  1305. [newWin makeFirstResponder:m_view];
  1306. [newWin setAcceptsMouseMovedEvents:YES];
  1307. [newWin makeKeyAndOrderFront:nil]; // -drawRect gets invoked here.
  1308. // - there is now a big white window on the screen.
  1309. // - black it out and flush it
  1310. ClearGLView();
  1311. [ newWin setViewsNeedDisplay:YES ];
  1312. [ m_view setNeedsDisplay: YES ];
  1313. if (!bWindowed)
  1314. {
  1315. [ newWin setLevel:NSMainMenuWindowLevel+1 ]; // move to front of stack
  1316. }
  1317. ClearGLView();
  1318. [ newWin setViewsNeedDisplay:YES ];
  1319. [ m_view setNeedsDisplay: YES ];
  1320. gGL = GetOpenGLEntryPoints(VoidFnPtrLookup_GlMgr);
  1321. // It is now safe to call any base GL entry point that's supplied by gGL.
  1322. // You still need to explicitly test for extension entry points, though!
  1323. // if you don't sleep, you don't ncessarily get everything flushed to screen before movie player jumps on it..
  1324. sleep(1);
  1325. //-----------------------------------------------------------------------------------------
  1326. // If old window exists, scrub it
  1327. if (m_window)
  1328. {
  1329. [ m_window close ];
  1330. [ m_window release ];
  1331. m_window = NULL;
  1332. }
  1333. // replace with newly made window
  1334. m_window = newWin;
  1335. //-----------------------------------------------------------------------------------------
  1336. // Get rid of the autorelease pool.
  1337. [pool release];
  1338. // clear current GL context...
  1339. [ NSOpenGLContext clearCurrentContext ];
  1340. //this->PumpWindowsMessageLoop();
  1341. m_fsEnable = !bWindowed;
  1342. return true;
  1343. }
  1344. PseudoNSGLContextPtr CCocoaMgr::GetNSGLContextForWindow( void* windowref )
  1345. {
  1346. WindowRef win = (WindowRef)windowref;
  1347. if (win==[m_window windowRef])
  1348. {
  1349. PseudoNSGLContextPtr nsctx = [ m_view openGLContext ];
  1350. Assert( nsctx != NULL );
  1351. return nsctx;
  1352. }
  1353. else
  1354. {
  1355. return NULL; // sorry, no idea
  1356. }
  1357. }
  1358. void* CCocoaMgr::GetWindowRef()
  1359. {
  1360. NSWindow *pWindow = (NSWindow*)m_window;
  1361. return [pWindow windowRef];
  1362. }
  1363. void CCocoaMgr::SetWindowFullScreen( bool bFullScreen, int nWidth, int nHeight )
  1364. {
  1365. }
  1366. bool CCocoaMgr::IsWindowFullScreen()
  1367. {
  1368. return m_fsEnable;
  1369. }
  1370. void CCocoaMgr::SetForbidMouseGrab( bool bForbidMouseGrab )
  1371. {
  1372. }
  1373. PseudoNSGLContextPtr CCocoaMgr::GetGLContextForWindow( void* windowref )
  1374. {
  1375. return GetNSGLContextForWindow(windowref);
  1376. }
  1377. PseudoNSGLContextPtr CCocoaMgr::GetMainContext()
  1378. {
  1379. PseudoNSGLContextPtr nsctx = [ m_view openGLContext ];
  1380. return nsctx;
  1381. }
  1382. PseudoNSGLContextPtr CCocoaMgr::CreateExtraContext()
  1383. {
  1384. return NULL;
  1385. }
  1386. void CCocoaMgr::DeleteContext( PseudoNSGLContextPtr hContext )
  1387. {
  1388. }
  1389. bool CCocoaMgr::MakeContextCurrent( PseudoNSGLContextPtr hContext )
  1390. {
  1391. return false;
  1392. }
  1393. void CCocoaMgr::SetMouseVisible( bool bState )
  1394. {
  1395. }
  1396. void CCocoaMgr::GetDisplaySize( uint *puiWidth, uint *puiHeight ) // Retrieve the size of the monitor (desktop)
  1397. {
  1398. }
  1399. void CCocoaMgr::GetNativeDisplayInfo( int nDisplay, uint &nWidth, uint &nHeight, uint &nRefreshHz )
  1400. {
  1401. GLMDisplayDB *db = GetDisplayDB();
  1402. GLMRendererInfo *pRenderInfo = ( *db->m_renderers )[ 0 ];
  1403. GLMDisplayInfo *pDisplayInfo = ( *pRenderInfo->m_displays )[ nDisplay ];
  1404. GLMDisplayMode *displayModeInfo = (*pDisplayInfo->m_modes)[ -1 ];
  1405. nRefreshHz = displayModeInfo->m_info.m_modeRefreshHz;
  1406. nWidth = displayModeInfo->m_info.m_modePixelWidth;
  1407. nHeight = displayModeInfo->m_info.m_modePixelHeight;
  1408. }
  1409. ConVar gl_swapdebug( "gl_swapdebug", "0");
  1410. ConVar gl_swaplimit( "gl_swaplimit", "0");
  1411. ConVar gl_swapinterval( "gl_swapinterval", "0");
  1412. ConVar gl_swaplimit_mt( "gl_swaplimit_mt", "3");
  1413. ConVar mac_fsbackground( "mac_fsbackground", "0");
  1414. ConVar mac_cursorwarp( "mac_cursorwarp", "1");
  1415. ConVar gl_blit_halfx( "gl_blit_halfx", "0" );
  1416. ConVar gl_blit_halfy( "gl_blit_halfy", "0" );
  1417. ConVar gl_disable_forced_vsync( "gl_disable_forced_vsync", "0" );
  1418. void CCocoaMgr::ShowPixels( CShowPixelsParams *params )
  1419. {
  1420. // this is (probably) not being called on the main thread
  1421. // send a message over for processing.
  1422. Assert( m_window != NULL );
  1423. Assert( m_view != NULL );
  1424. [ [ m_view openGLContext ] makeCurrentContext ];
  1425. if (m_frontPushCounter>0)
  1426. {
  1427. // we force the window to front after a mode change, on the first blit that comes through
  1428. [m_window makeFirstResponder:m_view];
  1429. [m_window setAcceptsMouseMovedEvents:YES];
  1430. [m_window makeKeyAndOrderFront:nil];
  1431. [ m_view update ];
  1432. // further, if now in fullscreen mode and SL, we set the frame again in case the user managed to scoot it..
  1433. if (m_fsEnable && !m_leopard)
  1434. {
  1435. [ m_window setFrame:m_fsFrame display:true ];
  1436. }
  1437. // and, reset the swap interval and limit
  1438. m_lastKnownSwapInterval = -1;
  1439. m_lastKnownSwapLimit = -1;
  1440. m_frontPushCounter--;
  1441. }
  1442. else
  1443. {
  1444. if (m_fsEnable)
  1445. {
  1446. NSInteger targetLevel = 0;
  1447. if (mac_fsbackground.GetInt() && !m_leopard) // SL or better, and user asked for FS to be visible as background when non-front app
  1448. {
  1449. [ m_window setHidesOnDeactivate: NO ];
  1450. // try to avoid resetting this every frame unless it needs some settin'
  1451. NSApplication *pApplication = (NSApplication*)m_application;
  1452. targetLevel = [pApplication isActive] ? NSMainMenuWindowLevel+1 : kCGDesktopWindowLevel;
  1453. }
  1454. else
  1455. {
  1456. if (!CommandLine()->FindParm("-glmdebugfullscreen"))
  1457. {
  1458. [ m_window setHidesOnDeactivate: YES ];
  1459. }
  1460. targetLevel = NSMainMenuWindowLevel+1;
  1461. }
  1462. if ( [m_window level] != targetLevel )
  1463. {
  1464. [ m_window setLevel:targetLevel ]; // move to front of stack
  1465. [ m_view update ];
  1466. }
  1467. }
  1468. else
  1469. {
  1470. [ m_window setHidesOnDeactivate: NO ];
  1471. }
  1472. }
  1473. // in the 10.6 world we go with the assumption that the context that is sending us pixels, was shared off of the
  1474. // view bridge's context. So there should be no need at all to create a new context - we can just target the view context
  1475. // in the window. This should work whether that context is fullscreen or not.
  1476. // about all we need to know is the size of the slab handed us, and the size of the backing store of the view context.
  1477. // note that on 10.6, we have the option of the view-context's backing store staying small even though the window/view is
  1478. // large.
  1479. // first look at the FS state and see if we need to flip
  1480. if ( (params->m_fsEnable!=0) != (m_fsEnable!=0) )
  1481. {
  1482. // flip it
  1483. // honor res changes here by resizing the backing store of the gl view
  1484. NSRect screenFrame = [[NSScreen mainScreen] frame];
  1485. if (params->m_fsEnable)
  1486. {
  1487. // go to FS... save the old windowed frame first?
  1488. NSRect oldWinFrame = [ m_window frame ];
  1489. SetWindowedFrame( &oldWinFrame, false ); // latch but do not act
  1490. SetFullscreenFrame( &screenFrame, true ); // use the latched rect from setup time (or consider grabbing a new one which could be more current)
  1491. }
  1492. else
  1493. {
  1494. // go to windowed.
  1495. // see if the m_winFrame is the same size as this inbound blit.
  1496. // if it's not, construct a new one.
  1497. NSRect winFrame = m_winFrame;
  1498. if ( (winFrame.size.width != params->m_width) || (winFrame.size.height != params->m_height) )
  1499. {
  1500. int sw = screenFrame.size.width;
  1501. int sh = screenFrame.size.height;
  1502. winFrame.origin.x = ((sw - params->m_width) / 2); // int math to truncate prior to assignment
  1503. winFrame.origin.y = ((sh - params->m_height) / 2); // int math to truncate prior to assignment
  1504. winFrame.size.width = params->m_width;
  1505. winFrame.size.height = params->m_height;
  1506. }
  1507. SetWindowedFrame( &winFrame, true ); // use the latched rect from last transition to FS (see above, we save it)
  1508. }
  1509. }
  1510. if (!params->m_onlySyncView)
  1511. {
  1512. // save old context
  1513. NSOpenGLContext *curr = [ NSOpenGLContext currentContext ];
  1514. // get target context
  1515. m_showPixelsCtx = [ m_view openGLContext ];
  1516. // make it current
  1517. [m_showPixelsCtx makeCurrentContext];
  1518. int swapInterval = 0;
  1519. int swapLimit = 0;
  1520. if (gl_swapdebug.GetInt())
  1521. {
  1522. // just jam through these debug convars every frame
  1523. // but they will be shock absorbed below
  1524. swapInterval = gl_swapinterval.GetInt();
  1525. swapLimit = gl_swaplimit.GetInt();
  1526. }
  1527. else
  1528. {
  1529. // jam through (sync&limit) = 1 or 0..
  1530. swapInterval = params->m_vsyncEnable ? 1 : 0;
  1531. swapLimit = 1; // params->m_vsyncEnable ? 1 : 0; // no good reason to turn off swap limit in normal user mode
  1532. // only do the funky forced vsync for NV on 10.6.4 and only if the bypass is not turned on
  1533. if (m_force_vsync && (gl_disable_forced_vsync.GetInt()==0))
  1534. {
  1535. swapInterval = 1;
  1536. swapLimit = 1;
  1537. }
  1538. }
  1539. // only touch them on changes, or right after a change in windowed/FS state
  1540. if ( (swapInterval!=m_lastKnownSwapInterval) || (swapLimit!=m_lastKnownSwapLimit) )
  1541. {
  1542. [m_showPixelsCtx setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
  1543. m_lastKnownSwapInterval = swapInterval;
  1544. if (swapLimit)
  1545. {
  1546. CGLEnable( CGLGetCurrentContext(), kCGLCESwapLimit );
  1547. }
  1548. else
  1549. {
  1550. CGLDisable( CGLGetCurrentContext(), kCGLCESwapLimit );
  1551. }
  1552. m_lastKnownSwapLimit = swapLimit;
  1553. if (gl_swapdebug.GetInt()) // only touch this with swap debug on for now
  1554. {
  1555. GLint maxMPswaps = gl_swaplimit_mt.GetInt();
  1556. CGLSetParameter( CGLGetCurrentContext(), kCGLCPMPSwapsInFlight, &maxMPswaps );
  1557. printf("\n ----- MT swap limit = %d \n", maxMPswaps );
  1558. }
  1559. printf("\n ##### swap interval = %d swap limit = %d #####\n", m_lastKnownSwapInterval, m_lastKnownSwapLimit );
  1560. fflush(stdout);
  1561. }
  1562. if (!params->m_noBlit)
  1563. {
  1564. if ( params->m_useBlit ) // FBO blit path - which is what we *should* be using. But if the params say no, then don't do it because the ext is not there.
  1565. {
  1566. // bind a quickie FBO to enclose the source texture
  1567. GLint myreadfb = 1000;
  1568. glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, myreadfb);
  1569. __checkgl__();
  1570. glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, 0); // to the default FB/backbuffer
  1571. __checkgl__();
  1572. // attach source tex to source FB
  1573. glFramebufferTexture2DEXT( GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, params->m_srcTexName, 0);
  1574. __checkgl__();
  1575. // blit
  1576. int srcxmin = 0;
  1577. int srcymin = 0;
  1578. int srcxmax = params->m_width;
  1579. int srcymax = params->m_height;
  1580. NSRect dstframe = [m_view frame];
  1581. // normal blit
  1582. int dstxmin = 0;
  1583. int dstymin = 0;
  1584. int dstxmax = dstframe.size.width;
  1585. int dstymax = dstframe.size.height;
  1586. if (gl_blit_halfx.GetInt())
  1587. {
  1588. // blit right half
  1589. srcxmin += srcxmax/2;
  1590. dstxmin += dstxmax/2;
  1591. }
  1592. if (gl_blit_halfy.GetInt())
  1593. {
  1594. // blit top half
  1595. // er, but top on screen is bottom of GL y coord range
  1596. srcymax /= 2;
  1597. dstymin += dstymax/2;
  1598. }
  1599. // go NEAREST if sizes match
  1600. GLenum filter = ( ((srcxmax-srcxmin)==(dstxmax-dstxmin)) && ((srcymax-srcymin)==(dstymax-dstymin)) ) ? GL_NEAREST : GL_LINEAR;
  1601. glBlitFramebufferEXT(
  1602. /* src min and maxes xy xy */ srcxmin, srcymin, srcxmax,srcymax,
  1603. /* dst min and maxes xy xy */ dstxmin, dstymax, dstxmax,dstymin, // note yflip here
  1604. GL_COLOR_BUFFER_BIT, filter );
  1605. __checkgl__();
  1606. // detach source tex
  1607. glFramebufferTexture2DEXT( GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);
  1608. __checkgl__();
  1609. glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, 0);
  1610. __checkgl__();
  1611. glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, 0); // to the default FB/backbuffer
  1612. __checkgl__();
  1613. }
  1614. else
  1615. {
  1616. // old blit - gets very dark output with sRGB sources... not good
  1617. bool texing = true;
  1618. glUseProgram(NULL);
  1619. glDisable( GL_DEPTH_TEST );
  1620. glDepthMask( GL_FALSE );
  1621. glActiveTexture( GL_TEXTURE0 );
  1622. if (texing)
  1623. {
  1624. Assert( glIsTexture (params->m_srcTexName) );
  1625. glEnable(GL_TEXTURE_2D);
  1626. glBindTexture( GL_TEXTURE_2D, params->m_srcTexName );
  1627. __checkgl__();
  1628. GLint width;
  1629. glGetTexLevelParameteriv( GL_TEXTURE_2D, //target
  1630. 0, //level,
  1631. GL_TEXTURE_WIDTH, //pname
  1632. &width
  1633. );
  1634. __checkgl__();
  1635. }
  1636. else
  1637. {
  1638. glBindTexture( GL_TEXTURE_2D, 0 );
  1639. __checkgl__();
  1640. glDisable( GL_TEXTURE_2D );
  1641. glColor4f( 1.0, 0.0, 0.0, 1.0 );
  1642. }
  1643. // immediate mode is fine for a simple textured quad
  1644. // later if we switch the Valve side to render into an RBO, then this would turn into an FBO blit
  1645. // note, do not check glGetError in between glBegin/glEnd, lol
  1646. // flipped
  1647. float topv = 0.0;
  1648. float botv = 1.0;
  1649. glBegin(GL_QUADS);
  1650. if (texing)
  1651. glTexCoord2f( 0.0, botv );
  1652. glVertex3f ( -1.0, -1.0, 0.0 );
  1653. if (texing)
  1654. glTexCoord2f( 1.0, botv );
  1655. glVertex3f ( 1.0, -1.0, 0.0 );
  1656. if (texing)
  1657. glTexCoord2f( 1.0, topv );
  1658. glVertex3f ( 1.0, 1.0, 0.0 );
  1659. if (texing)
  1660. glTexCoord2f( 0.0, topv );
  1661. glVertex3f ( -1.0, 1.0, 0.0 );
  1662. glEnd();
  1663. __checkgl__();
  1664. if (texing)
  1665. {
  1666. glBindTexture( GL_TEXTURE_2D, 0 );
  1667. __checkgl__();
  1668. glDisable(GL_TEXTURE_2D);
  1669. }
  1670. }
  1671. }
  1672. [m_showPixelsCtx flushBuffer];
  1673. m_lastShownPixels = *params;
  1674. CFastTimer tm;
  1675. tm.Start();
  1676. [curr makeCurrentContext];
  1677. m_flPrevGLSwapWindowTime = tm.GetDurationInProgress().GetMillisecondsF();
  1678. }
  1679. }
  1680. void CCocoaMgr::SetWindowedFrame( NSRect *frame, bool forEffect )
  1681. {
  1682. // this function has to do different things depending on whether it's Leopard or Snow Leopard.
  1683. if (frame)
  1684. {
  1685. m_winFrame = *frame;
  1686. }
  1687. if (forEffect)
  1688. {
  1689. // ask for window to be bumped to front on next few blits (and scrubbed, if a -drawRect lands on us)
  1690. m_frontPushCounter = 3;
  1691. if (m_fsEnable)
  1692. {
  1693. // -- transitioning into windowed --
  1694. if (m_leopard)
  1695. {
  1696. // re create window as non full screen
  1697. // FIXME need to get proper title
  1698. InternalCreateWindow( NULL, true, &m_winFrame ); // NULL means "use old title", bWindowed = true
  1699. SetFullscreenUIMode( false );
  1700. }
  1701. else
  1702. {
  1703. // SL or later
  1704. // change the style mask and layering, then change the frame
  1705. [ m_window setStyleMask:s_windowedStyleMask ];
  1706. [ m_window setLevel: NSNormalWindowLevel ];
  1707. // fix the title back..
  1708. [ m_window setTitle:[NSString stringWithUTF8String:m_windowTitle]];
  1709. }
  1710. // also do these tweaks
  1711. [ m_window setOpaque:YES ];
  1712. [ m_window setHidesOnDeactivate:NO ];
  1713. m_fsEnable = false;
  1714. }
  1715. // now set the frame
  1716. NSRect frameRect = [ m_window frameRectForContentRect:m_winFrame];
  1717. [ m_window setFrame:frameRect display:true ];
  1718. // make sure it's in front
  1719. [ m_window makeKeyAndOrderFront:nil ];
  1720. //ClearGLView();
  1721. }
  1722. }
  1723. void CCocoaMgr::SetFullscreenFrame( NSRect *frame, bool forEffect )
  1724. {
  1725. // this function has to do different things depending on whether it's Leopard or Snow Leopard.
  1726. if (frame)
  1727. {
  1728. m_fsFrame = *frame;
  1729. }
  1730. if (forEffect)
  1731. {
  1732. // ask for window to be bumped to front on next few blits
  1733. m_frontPushCounter = 3;
  1734. if (!m_fsEnable)
  1735. {
  1736. // -- transitioning into fullscreen --
  1737. if (m_leopard)
  1738. {
  1739. // re-create the window as fullscreen
  1740. InternalCreateWindow( NULL, false, &m_fsFrame ); // NULL means use old title, bWindowed = false
  1741. SetFullscreenUIMode( true );
  1742. }
  1743. else
  1744. {
  1745. [ m_window setFrame:m_fsFrame display:false ];
  1746. // SL or better
  1747. // change the style mask and layering, then change the frame
  1748. [ m_window setStyleMask:NSBorderlessWindowMask ]; // slam the style mask
  1749. }
  1750. if (!CommandLine()->FindParm("-glmdebugfullscreen"))
  1751. {
  1752. [ m_window setLevel:NSMainMenuWindowLevel+1 ]; // move to front of stack
  1753. [ m_window setHidesOnDeactivate: mac_fsbackground.GetInt() ? NO : YES ];
  1754. }
  1755. else
  1756. {
  1757. [ m_window setHidesOnDeactivate:NO ];
  1758. }
  1759. [ m_window setOpaque:YES ];
  1760. // now set the frame
  1761. [ m_window setFrame:m_fsFrame display:true ];
  1762. m_fsEnable = true;
  1763. }
  1764. // make sure it's in front
  1765. [ m_window makeKeyAndOrderFront:nil /*m_window*/];
  1766. //ClearGLView();
  1767. }
  1768. }
  1769. void CCocoaMgr::ClearGLView( void )
  1770. {
  1771. if (m_view)
  1772. {
  1773. [[m_view openGLContext] makeCurrentContext];
  1774. GLfloat clear_color[4] = { 0.05f, 0.05f, 0.05f, 1.0f }; // near black
  1775. glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
  1776. glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT+GL_STENCIL_BUFFER_BIT);
  1777. glFinish();
  1778. [[m_view openGLContext] flushBuffer];
  1779. }
  1780. }
  1781. // we should probably ignore this if we're in fullscreen mode
  1782. void CCocoaMgr::MoveWindow( int x, int y )
  1783. {
  1784. if (!m_fsEnable)
  1785. {
  1786. NSWindow *pWindow = (NSWindow*)m_window;
  1787. [pWindow setFrameOrigin:NSMakePoint( x, y )];
  1788. }
  1789. }
  1790. // we should probably ignore this if we're in fullscreen mode
  1791. void CCocoaMgr::SizeWindow( int width, int tall )
  1792. {
  1793. if (!m_fsEnable)
  1794. {
  1795. NSWindow *pWindow = (NSWindow*)m_window;
  1796. [pWindow setContentSize:NSMakeSize( width, tall )];
  1797. }
  1798. // We don't want to clear on every resize because if we are threaded rendering, this will collide.
  1799. // This static just means we only ever clear when our window is created
  1800. // This fixes ALT-TAB
  1801. static bool firstClear = true;
  1802. if (firstClear)
  1803. {
  1804. ClearGLView();
  1805. firstClear = false;
  1806. }
  1807. }
  1808. int CCocoaMgr::GetEvents( CCocoaEvent *pEvents, int nMaxEventsToReturn, bool debugEvent )
  1809. {
  1810. m_CocoaEventsMutex.Lock();
  1811. CUtlLinkedList<CCocoaEvent,int> &queue = debugEvent ? m_CocoaEvents : m_DebugEvents;
  1812. int nAvailable = queue.Count();
  1813. int nToWrite = MIN( nAvailable, nMaxEventsToReturn );
  1814. CCocoaEvent *pCurEvent = pEvents;
  1815. for ( int i=0; i < nToWrite; i++ )
  1816. {
  1817. int iHead = queue.Head();
  1818. memcpy( pCurEvent, &queue[iHead], sizeof( CCocoaEvent ) );
  1819. queue.Remove( iHead );
  1820. ++pCurEvent;
  1821. }
  1822. m_CocoaEventsMutex.Unlock();
  1823. return nToWrite;
  1824. }
  1825. void CCocoaMgr::SetCursorPosition( int x, int y )
  1826. {
  1827. // scale, because screen coords may not match engine's video mode resolution
  1828. float modeWidth = m_lastShownPixels.m_width;
  1829. float modeHeight = m_lastShownPixels.m_height;
  1830. if ( (modeWidth != m_view.frame.size.width ) && (modeHeight != m_view.frame.size.height ) )
  1831. {
  1832. // apply scale such that our coords make sense back in the engine's world
  1833. // multiply by engine extent, divide by window extent
  1834. x *= m_view.frame.size.width; x /= modeWidth;
  1835. y *= m_view.frame.size.height; y /= modeHeight;
  1836. }
  1837. // Source view coords (y down) -> NSView coordinates (Y up)
  1838. NSPoint pt;
  1839. pt.x = x;
  1840. pt.y = m_view.frame.size.height - y; // Make Y go up.
  1841. // NSView coords -> window base coords
  1842. NSPoint windowCoords = [m_view convertPoint:pt toView:nil];
  1843. // window base coords -> screen coords (Y up)
  1844. NSPoint screenCoords = [m_window convertBaseToScreen:windowCoords];
  1845. // screen coords (Y up) -> CG screen coords (Y down)
  1846. CGPoint cgpt;
  1847. cgpt.x = screenCoords.x;
  1848. cgpt.y = [m_window screen].frame.size.height - screenCoords.y;
  1849. // if you warp the cursor while the titlebar is grabbed (after alt-tab) then the windows goes nuts
  1850. // with its positioning, so only warp if we aren't in that state
  1851. if ( !s_bBlockWarpCursor )
  1852. {
  1853. if( mac_cursorwarp.GetInt() )
  1854. {
  1855. CGWarpMouseCursorPosition( cgpt );
  1856. }
  1857. }
  1858. }
  1859. NSPoint CCocoaMgr::ScreenCoordsToSourceWindowCoords( NSView *pView, NSPoint screenCoord )
  1860. {
  1861. // Go to Window base coordinates.
  1862. NSPoint windowBase = [[pView window] convertScreenToBase:screenCoord];
  1863. // Convert from Window base to view coordinates.
  1864. NSPoint viewCoords = [pView convertPoint:windowBase fromView:nil];
  1865. // Flip Y (they're reporting Y going up whereas Source wants it going down).
  1866. viewCoords.y = pView.frame.size.height - viewCoords.y;
  1867. // scale, because screen coords may not match engine's video mode resolution
  1868. float modeWidth = m_lastShownPixels.m_width;
  1869. float modeHeight = m_lastShownPixels.m_height;
  1870. if ( (modeWidth != m_view.frame.size.width ) && (modeHeight != m_view.frame.size.height ) )
  1871. {
  1872. // apply scale such that our coords make sense back in the engine's world
  1873. // multiply by engine extent, divide by window extent
  1874. viewCoords.x *= modeWidth; viewCoords.x /= pView.frame.size.width;
  1875. viewCoords.y *= modeHeight; viewCoords.y /= pView.frame.size.height;
  1876. }
  1877. else if ( modeWidth == 0.0 || modeHeight == 0.0 )
  1878. {
  1879. // shrug. an event got to us before the first blit, so we don't know how to scale it.
  1880. // just slam it to 0,0.
  1881. viewCoords.x = viewCoords.y = 0;
  1882. }
  1883. return viewCoords;
  1884. }
  1885. void CCocoaMgr::StopRunLoop()
  1886. {
  1887. // Always have an autorelease pool handy...
  1888. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1889. NSApplication *pApplication = (NSApplication*)m_application;
  1890. [pApplication performSelectorOnMainThread:@selector(stop:) withObject:pApplication waitUntilDone:true];
  1891. // NSApplication::stop only sets a flag telling it to exit the run loop AFTER the next event gets processed,
  1892. // so let's generate a dummy event.
  1893. PostDummyEvent();
  1894. [pool release];
  1895. }
  1896. static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
  1897. {
  1898. CFURLRef the_url = NULL;
  1899. CGImageSourceRef source_ref = NULL;
  1900. CFStringRef cf_string = NULL;
  1901. cf_string = CFStringCreateWithCString( NULL, the_path, kCFStringEncodingUTF8 );
  1902. if(!cf_string)
  1903. {
  1904. return NULL;
  1905. }
  1906. the_url = CFURLCreateWithFileSystemPath(NULL, cf_string, kCFURLPOSIXPathStyle, false );
  1907. CFRelease(cf_string);
  1908. if(!the_url)
  1909. {
  1910. return NULL;
  1911. }
  1912. source_ref = CGImageSourceCreateWithURL( the_url, NULL );
  1913. CFRelease(the_url);
  1914. return source_ref;
  1915. }
  1916. static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
  1917. {
  1918. CGImageRef image_ref = NULL;
  1919. if(NULL == image_source)
  1920. {
  1921. return NULL;
  1922. }
  1923. // Get the first item in the image source (some image formats may
  1924. // contain multiple items).
  1925. image_ref = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
  1926. return image_ref;
  1927. }
  1928. void CCocoaMgr::SetApplicationIcon( const char *pchAppIconFile )
  1929. {
  1930. CGImageRef imgRef = CreateCGImageFromCGImageSource( CreateCGImageSourceFromFile( pchAppIconFile ) );
  1931. if ( imgRef )
  1932. {
  1933. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1934. NSBitmapImageRep* bir = [[NSBitmapImageRep alloc] initWithCGImage:imgRef];
  1935. NSImage* img = [[NSImage alloc] initWithData:[ bir TIFFRepresentation]];
  1936. [NSApp setApplicationIconImage:img];
  1937. [img release];
  1938. [bir release];
  1939. [pool release];
  1940. }
  1941. }
  1942. void CCocoaMgr::FinishLaunchingApplication()
  1943. {
  1944. ThreadSetDebugName( "CCocoaMgr" );
  1945. m_nRunLoopThreadID = ThreadGetCurrentId();
  1946. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1947. NSApplication *pApplication = (NSApplication*)m_application;
  1948. // Hookup our delegate.
  1949. m_pCocoaBridge = [[CocoaBridge alloc] init];
  1950. [m_pCocoaBridge retain];
  1951. m_pCocoaBridge->m_pCocoaMgr = this;
  1952. [pApplication setDelegate:m_pCocoaBridge];
  1953. [pApplication finishLaunching];
  1954. [pool release];
  1955. }
  1956. void CCocoaMgr::PumpWindowsMessageLoop()
  1957. {
  1958. // NSOpenGLContext *curr = [ NSOpenGLContext currentContext ];
  1959. // [ NSOpenGLContext clearCurrentContext ];
  1960. bool bWorkToDo = true;
  1961. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1962. NSApplication *pApplication = (NSApplication*)m_application;
  1963. while ( bWorkToDo )
  1964. {
  1965. NSEvent *event = [pApplication nextEventMatchingMask: NSAnyEventMask
  1966. untilDate: [NSDate distantPast]
  1967. inMode: NSDefaultRunLoopMode
  1968. dequeue: YES];
  1969. if (event != nil)
  1970. {
  1971. [pApplication sendEvent: event];
  1972. }
  1973. else
  1974. {
  1975. bWorkToDo = false;
  1976. }
  1977. //[event release]; // 96.5K malloc
  1978. }
  1979. [pool release];
  1980. // [ curr makeCurrentContext ];
  1981. }
  1982. void CCocoaMgr::WaitUntilUserInput( int msSleepTime )
  1983. {
  1984. if ( !msSleepTime || msSleepTime < 0 )
  1985. return;
  1986. // NSOpenGLContext *curr = [ NSOpenGLContext currentContext ];
  1987. // [ NSOpenGLContext clearCurrentContext ];
  1988. NSApplication *pApplication = (NSApplication*)m_application;
  1989. NSEvent *event = [pApplication nextEventMatchingMask: NSAnyEventMask
  1990. untilDate: [NSDate dateWithTimeIntervalSinceNow:(double)msSleepTime/1000 ]
  1991. inMode: NSDefaultRunLoopMode
  1992. dequeue: YES];
  1993. if (event != nil)
  1994. {
  1995. [pApplication sendEvent: event];
  1996. }
  1997. // [ curr makeCurrentContext ];
  1998. }
  1999. // ------------------------------------------------------------------------------------ //
  2000. // Access to private Symbolication framework, for stack crawling
  2001. // ------------------------------------------------------------------------------------ //
  2002. // this is all pieced together using class-dump and some other references:
  2003. // http://seriot.ch/resources/dynamic_iPhone_headers/2_2_1/
  2004. // http://seriot.ch/resources/dynamic_iPhone_headers/2_2_1/VMUSymbolicator.h
  2005. // http://seriot.ch/resources/dynamic_iPhone_headers/2_2_1/VMUSymbol.h
  2006. // http://www.cocoadev.com/index.pl?StackTraces
  2007. struct _VMURange {
  2008. unsigned long long location;
  2009. unsigned long long length;
  2010. };
  2011. @interface VMUAddressRange : NSObject {
  2012. struct _VMURange _addressRange;
  2013. }
  2014. @end
  2015. @interface VMUSymbol : VMUAddressRange {
  2016. NSString *_name;
  2017. NSString *_mangledName;
  2018. void *_owner;
  2019. unsigned int _flags;
  2020. }
  2021. - (id)name;
  2022. - (id)sourceInfoForAddress:(unsigned long long)address;
  2023. @end
  2024. @interface VMUSymbolicator : NSObject {
  2025. NSMutableArray *_symbolOwners;
  2026. NSArray *_symbolOwnerAddressRanges;
  2027. NSString *_path;
  2028. void *_machTaskContainer;
  2029. BOOL _isProtected;
  2030. }
  2031. + (id)symbolicatorForPid:(int)fp8;
  2032. + (VMUSymbolicator*)symbolicatorForTask:(unsigned)task;
  2033. - (id)symbolForAddress:(unsigned long long)address;
  2034. @end
  2035. struct _NSZone { };
  2036. @interface VMUSourceInfo : VMUAddressRange <NSCopying>
  2037. {
  2038. NSString *_path;
  2039. NSUInteger _lineNumber;
  2040. NSUInteger _fileOffset;
  2041. }
  2042. + (id)sourceInfoWithPath:(id)arg1 addressRange:(_VMURange)arg2 lineNumber:(NSUInteger)arg3 fileOffset:(NSUInteger)arg4;
  2043. - (id)initWithPath:(id)arg1 addressRange:(_VMURange)arg2 lineNumber:(NSUInteger)arg3 fileOffset:(NSUInteger)arg4;
  2044. - (id)path;
  2045. - (id)fileName;
  2046. - (NSUInteger)lineNumber;
  2047. - (NSUInteger)fileOffset;
  2048. - (_VMURange)addressRange;
  2049. - (NSInteger)compare:(id)arg1;
  2050. - (BOOL)isEqualToSourceInfo:(id)arg1;
  2051. - (id)description;
  2052. - (void)dealloc;
  2053. - (id)copyWithZone:(_NSZone*)arg1;
  2054. @end
  2055. static id _symbolicator = nil;
  2056. #define SYMBOLICATION_FRAMEWORK @"/System/Library/PrivateFrameworks/Symbolication.framework"
  2057. /* redundant ?
  2058. @interface NSObject (SymbolicatorAPIs)
  2059. - (id)symbolicatorForTask:(mach_port_t)task;
  2060. - (id)symbolForAddress:(uint64_t)address;
  2061. - (id)sourceInfoForAddress:(unsigned long long)address;
  2062. - (void)forceFullSymbolExtraction;
  2063. - (_VMURange)addressRange;
  2064. @end
  2065. */
  2066. static inline id rb_objc_symbolicator(void)
  2067. {
  2068. if (_symbolicator == nil)
  2069. {
  2070. NSError *error;
  2071. if (![[NSBundle bundleWithPath:SYMBOLICATION_FRAMEWORK] loadAndReturnError:&error])
  2072. {
  2073. NSLog(@"Cannot load Symbolication.framework: %@", error);
  2074. abort();
  2075. }
  2076. Class VMUSymbolicator = NSClassFromString(@"VMUSymbolicator");
  2077. _symbolicator = [VMUSymbolicator symbolicatorForTask:mach_task_self()];
  2078. assert(_symbolicator != nil);
  2079. }
  2080. return _symbolicator;
  2081. }
  2082. void CCocoaMgr::GetStackCrawl( CStackCrawlParams *params )
  2083. {
  2084. params->m_crawlText[0] = 0;
  2085. char *cursor = params->m_crawlText;
  2086. bool shortenFuncNames = true;
  2087. VMUSymbolicator * symbolicator = rb_objc_symbolicator();
  2088. NSArray * addresses = [NSThread callStackReturnAddresses];
  2089. params->m_frameCount = 0;
  2090. for (NSNumber * address in addresses)
  2091. {
  2092. if (params->m_frameCount < params->m_frameLimit)
  2093. {
  2094. VMUSymbol * symbol = [symbolicator symbolForAddress:[address unsignedLongLongValue]];
  2095. VMUSourceInfo *sourceinfo = [symbol sourceInfoForAddress:[address unsignedLongLongValue]];
  2096. NSString *funcname = [symbol name];
  2097. NSString *funcpath = [sourceinfo path];
  2098. NSUInteger funcline = [sourceinfo lineNumber];
  2099. char tempname[1000];
  2100. char temppath[1000];
  2101. tempname[0] = 0;
  2102. temppath[0] = 0;
  2103. [ funcname getCString:tempname maxLength:(sizeof(tempname)) encoding:[NSString defaultCStringEncoding ] ];
  2104. if(shortenFuncNames)
  2105. {
  2106. // find the first '(' and term the string there
  2107. char *leftparen = strchr( tempname, '(' );
  2108. if(leftparen)
  2109. {
  2110. *leftparen = 0;
  2111. }
  2112. }
  2113. [ funcpath getCString:temppath maxLength:(sizeof(temppath)) encoding:[NSString defaultCStringEncoding ] ];
  2114. Q_snprintf( cursor, sizeof(params->m_crawlText)-(cursor-params->m_crawlText)-2, "%s -- %s:%d", tempname, temppath, funcline );
  2115. // log this entry
  2116. params->m_crawl[ params->m_frameCount ] = (void *)[ address unsignedLongLongValue ];
  2117. params->m_crawlNames[ params->m_frameCount ] = cursor;
  2118. cursor += strlen( cursor );
  2119. *cursor++ = 0;
  2120. // unsure if these need to be freed or not.
  2121. //[funcname release];
  2122. //[funcpath release];
  2123. //[sourceinfo release];
  2124. //[symbol release];
  2125. params->m_frameCount++;
  2126. }
  2127. }
  2128. }
  2129. // ------------------------------------------------------------------------------------ //
  2130. void CCocoaMgr::PostDummyEvent()
  2131. {
  2132. NSPoint location = {0,0};
  2133. NSEvent *pEvent = [NSEvent otherEventWithType:NSApplicationDefined
  2134. location:location
  2135. modifierFlags:0
  2136. timestamp:0
  2137. windowNumber:0
  2138. context:nil
  2139. subtype:0
  2140. data1:0
  2141. data2:0
  2142. ];
  2143. NSApplication *pApplication = (NSApplication*)m_application;
  2144. [pApplication postEvent:pEvent atStart:false];
  2145. }
  2146. bool CCocoaMgr::IsDebugEvent( CCocoaEvent& event )
  2147. {
  2148. bool result = false;
  2149. #if GLMDEBUG
  2150. // simple rule for now, if the option key is involved, it's a debug key
  2151. // but only if GLM debugging is builtin
  2152. result |= ( (event.m_EventType == CocoaEvent_KeyDown) && ((event.m_ModifierKeyMask & (1<<eAltKey))!=0) );
  2153. #endif
  2154. return result;
  2155. }
  2156. void CCocoaMgr::WaitForApplicationToFinishLaunching()
  2157. {
  2158. m_AppObjectInitialized.Wait();
  2159. }
  2160. void CCocoaMgr::applicationDidFinishLaunching()
  2161. {
  2162. m_AppObjectInitialized.Set();
  2163. }
  2164. void CCocoaMgr::applicationWillTerminate()
  2165. {
  2166. Msg( "\n** CCocoaMgr::applicationWillTerminate\n\n" );
  2167. }
  2168. bool CCocoaMgr::Connect( CreateInterfaceFn factory )
  2169. {
  2170. return true;
  2171. }
  2172. void CCocoaMgr::Disconnect()
  2173. {
  2174. }
  2175. void *CCocoaMgr::QueryInterface( const char *pInterfaceName )
  2176. {
  2177. if ( !Q_stricmp( pInterfaceName, COCOAMGR_INTERFACE_VERSION ) )
  2178. return this;
  2179. return NULL;
  2180. }
  2181. // Init, shutdown
  2182. InitReturnVal_t CCocoaMgr::Init()
  2183. {
  2184. return INIT_OK;
  2185. }
  2186. void CCocoaMgr::Shutdown()
  2187. {
  2188. }
  2189. @interface SteamThreadSafetyObject : NSObject {}
  2190. - (SteamThreadSafetyObject *) initThreadSafety;
  2191. - (void) threadSafetyEntry;
  2192. @end
  2193. @implementation SteamThreadSafetyObject
  2194. - (SteamThreadSafetyObject *) initThreadSafety
  2195. {
  2196. // forcing an NSThread to spawn, even for an empty method, will switch Cocoa to thread-safe mode.
  2197. self = [super init];
  2198. [NSThread detachNewThreadSelector:@selector(threadSafetyEntry:)
  2199. toTarget:self withObject:nil];
  2200. return self;
  2201. }
  2202. - (void) threadSafetyEntry {}
  2203. @end
  2204. void macMakeCocoaThreadSafe(void)
  2205. {
  2206. SteamThreadSafetyObject *obj = [[SteamThreadSafetyObject alloc] init];
  2207. [obj release];
  2208. }
  2209. // ------------------------------------------------------------------------------------ //
  2210. // ValveCocoaMain implementation.
  2211. // ------------------------------------------------------------------------------------ //
  2212. CThreadEvent g_AppObjectTerminated;
  2213. CThreadEvent g_MainFunctionThreadExiting;
  2214. class CAppSystemGroup;
  2215. struct MainFunctionThreadArgs_t
  2216. {
  2217. CAppSystemGroup *pApp;
  2218. int m_nReturnValue;
  2219. };
  2220. uintp MainFunctionThread( void *pParam )
  2221. {
  2222. DeclareCurrentThreadIsMainThread();
  2223. void *pPool = macMakeAutoreleasePool();
  2224. // Now run the Valve stuff.
  2225. MainFunctionThreadArgs_t *pArgs = (MainFunctionThreadArgs_t*)pParam;
  2226. pArgs->m_nReturnValue = pArgs->pApp->Run();
  2227. // Stop the NSApplication run loop and wait for it to stop.
  2228. g_CocoaMgr.StopRunLoop();
  2229. // Synchronize the Valve thread and the main() thread exit.
  2230. g_MainFunctionThreadExiting.Set();
  2231. macReleaseAutoreleasePool( pPool );
  2232. return 0;
  2233. }
  2234. extern "C" int ValveCocoaMain( CAppSystemGroup *pApp )
  2235. {
  2236. // Spawn a thread for all the normal Valve stuff.
  2237. // This is Valve's main() thread as far as it's concerned.
  2238. MainFunctionThreadArgs_t args;
  2239. args.pApp = pApp;
  2240. void *pPool = macMakeAutoreleasePool();
  2241. macMakeCocoaThreadSafe();
  2242. // Create the NSApplication object and run it.
  2243. // This will block until the application calls CCocoaMgr::StopRunLoop.
  2244. // it will return false if we did this already, in which case we will skip the "finish launching" stuff.
  2245. if (g_CocoaMgr.CreateApplicationObject())
  2246. {
  2247. //only do this once..
  2248. g_CocoaMgr.FinishLaunchingApplication();
  2249. }
  2250. MainFunctionThread( &args );
  2251. // Synchronize the Valve thread and the main() thread exit.
  2252. g_AppObjectTerminated.Set();
  2253. macReleaseAutoreleasePool( pPool );
  2254. return args.m_nReturnValue;
  2255. }
  2256. // ------------------------------------------------------------------------------------ //
  2257. // GLMDisplayDB stuff which is going to live in CocoaMgr's module from now on..
  2258. //===============================================================================
  2259. // GLMDisplayMode, GLMDisplayInfo, GLMRendererInfo, GLMDisplayDB methods
  2260. GLMDisplayMode::GLMDisplayMode( uint width, uint height, uint refreshHz )
  2261. {
  2262. m_info.m_modePixelWidth = width;
  2263. m_info.m_modePixelHeight = height;
  2264. m_info.m_modeRefreshHz = refreshHz;
  2265. }
  2266. GLMDisplayMode::~GLMDisplayMode()
  2267. {
  2268. // empty
  2269. }
  2270. void GLMDisplayMode::Dump( int which )
  2271. {
  2272. GLMPRINTF(("\n # %-2d width=%-4d height=%-4d refreshHz=%-2d", which, m_info.m_modePixelWidth, m_info.m_modePixelHeight, m_info.m_modeRefreshHz ));
  2273. }
  2274. //===============================================================================
  2275. GLMDisplayInfo::GLMDisplayInfo( CGDirectDisplayID displayID, CGOpenGLDisplayMask displayMask )
  2276. {
  2277. m_info.m_cgDisplayID = displayID;
  2278. m_info.m_glDisplayMask = displayMask;
  2279. // extract info about this display such as pixel width and height
  2280. m_info.m_displayPixelWidth = (uint)CGDisplayPixelsWide( m_info.m_cgDisplayID );
  2281. m_info.m_displayPixelHeight = (uint)CGDisplayPixelsHigh( m_info.m_cgDisplayID );
  2282. m_modes = NULL;
  2283. }
  2284. GLMDisplayInfo::~GLMDisplayInfo( void )
  2285. {
  2286. if (m_modes)
  2287. {
  2288. // delete all the new'd display modes
  2289. FOR_EACH_VEC( *m_modes, i )
  2290. {
  2291. delete (*this->m_modes)[i];
  2292. }
  2293. delete m_modes;
  2294. m_modes = NULL;
  2295. }
  2296. }
  2297. extern "C" int DisplayModeSortFunction( GLMDisplayMode * const *A, GLMDisplayMode * const *B )
  2298. {
  2299. int bigger = -1;
  2300. int smaller = 1; // adjust these for desired ordering
  2301. // check refreshrate - higher should win
  2302. if ( (*A)->m_info.m_modeRefreshHz > (*B)->m_info.m_modeRefreshHz )
  2303. {
  2304. return bigger;
  2305. }
  2306. else if ( (*A)->m_info.m_modeRefreshHz < (*B)->m_info.m_modeRefreshHz )
  2307. {
  2308. return smaller;
  2309. }
  2310. // check area - larger mode should win
  2311. int areaa = (*A)->m_info.m_modePixelWidth * (*A)->m_info.m_modePixelHeight;
  2312. int areab = (*B)->m_info.m_modePixelWidth * (*B)->m_info.m_modePixelHeight;
  2313. if ( areaa > areab )
  2314. {
  2315. return bigger;
  2316. }
  2317. else if ( areaa < areab )
  2318. {
  2319. return smaller;
  2320. }
  2321. return 0; // equal rank
  2322. }
  2323. void GLMDisplayInfo::PopulateModes( void )
  2324. {
  2325. Assert( !m_modes );
  2326. m_modes = new CUtlVector< GLMDisplayMode* >;
  2327. CFArrayRef modeList;
  2328. // CGDisplayErr cgderr;
  2329. CFDictionaryRef cgvidmode;
  2330. CFNumberRef number;
  2331. CFBooleanRef boolean;
  2332. modeList = CGDisplayAvailableModes( m_info.m_cgDisplayID );
  2333. if ( modeList != NULL )
  2334. {
  2335. // examine each mode
  2336. CFIndex count = CFArrayGetCount( modeList );
  2337. for (CFIndex i = 0; i < count; i++)
  2338. {
  2339. long modeHeight = 0, modeWidth = 0;
  2340. long depth = 0;
  2341. long refreshrate = 0;
  2342. Boolean usable, stretched = false;
  2343. // grab the mode dictionary
  2344. cgvidmode = (CFDictionaryRef)CFArrayGetValueAtIndex( modeList, i);
  2345. // grab mode params we need
  2346. number = (CFNumberRef)CFDictionaryGetValue(cgvidmode, kCGDisplayBitsPerPixel);
  2347. CFNumberGetValue(number, kCFNumberLongType, &depth);
  2348. boolean = (CFBooleanRef)CFDictionaryGetValue(cgvidmode, kCGDisplayModeUsableForDesktopGUI) ;
  2349. usable = CFBooleanGetValue(boolean);
  2350. boolean = (CFBooleanRef)CFDictionaryGetValue(cgvidmode, kCGDisplayModeIsStretched);
  2351. if (NULL != boolean)
  2352. {
  2353. stretched = CFBooleanGetValue(boolean);
  2354. }
  2355. if ( usable && (!stretched) && (depth==32) )
  2356. {
  2357. // we're going to log this mode to the mode table.
  2358. // get height of mode
  2359. number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayHeight );
  2360. CFNumberGetValue(number, kCFNumberLongType, &modeHeight);
  2361. // get width of mode
  2362. number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayWidth );
  2363. CFNumberGetValue(number, kCFNumberLongType, &modeWidth);
  2364. // get refresh rate of mode
  2365. number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayRefreshRate );
  2366. double flrefreshrate = 0.0f;
  2367. CFNumberGetValue( number, kCFNumberDoubleType, &flrefreshrate );
  2368. refreshrate = (int)flrefreshrate;
  2369. // exclude silly small modes
  2370. if ( (modeHeight >= 384) && (modeWidth >= 512) )
  2371. {
  2372. GLMDisplayMode *newmode = new GLMDisplayMode( modeWidth, modeHeight, refreshrate );
  2373. m_modes->AddToTail( newmode );
  2374. }
  2375. }
  2376. }
  2377. }
  2378. // now sort the modes
  2379. // primary key is refresh rate
  2380. // secondary key is area
  2381. m_modes->Sort( DisplayModeSortFunction );
  2382. }
  2383. void GLMDisplayInfo::Dump( int which )
  2384. {
  2385. GLMPRINTF(("\n #%d: GLMDisplayInfo @ %p, cg-id=%08x display-mask=%08x pixwidth=%d pixheight=%d", which, this, m_info.m_cgDisplayID, m_info.m_glDisplayMask, m_info.m_displayPixelWidth, m_info.m_displayPixelHeight ));
  2386. FOR_EACH_VEC( *m_modes, i )
  2387. {
  2388. (*m_modes)[i]->Dump(i);
  2389. }
  2390. }
  2391. //===============================================================================
  2392. GLMRendererInfo::GLMRendererInfo( GLMRendererInfoFields *info )
  2393. {
  2394. NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init ];
  2395. // absorb info obtained so far by caller
  2396. m_info = *info;
  2397. m_displays = NULL;
  2398. // gather more info using a dummy context
  2399. unsigned int attribs[] =
  2400. {
  2401. kCGLPFADoubleBuffer, kCGLPFANoRecovery, kCGLPFAAccelerated,
  2402. kCGLPFADepthSize, 0,
  2403. kCGLPFAColorSize, 32,
  2404. kCGLPFARendererID, info->m_rendererID,
  2405. 0
  2406. };
  2407. NSOpenGLPixelFormat *pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*)attribs];
  2408. NSOpenGLContext *nsglCtx = [[NSOpenGLContext alloc] initWithFormat: pixFmt shareContext: NULL ];
  2409. [nsglCtx makeCurrentContext]; // this is a no-op if nsglCtx is null! no context bound.
  2410. // run queries.
  2411. char *gl_ext_string = (char*)glGetString(GL_EXTENSIONS);
  2412. uint vers = m_info.m_osComboVersion;
  2413. // avoid crashing due to strstr'ing NULL pointer returned from glGetString
  2414. if (!gl_ext_string)
  2415. gl_ext_string = "";
  2416. // effectively blacklist the renderer if it doesn't actually work; sort it to back of list
  2417. if ( !nsglCtx )
  2418. {
  2419. m_info.m_vidMemory = 1;
  2420. m_info.m_texMemory = 1;
  2421. }
  2422. //-------------------------------------------------------------------
  2423. // booleans
  2424. //-------------------------------------------------------------------
  2425. // gamma writes.
  2426. m_info.m_hasGammaWrites = true;
  2427. if ( vers < 0x000A0600 ) // pre 10.6.0, no SRGB write - see http://developer.apple.com/graphicsimaging/opengl/capabilities/GLInfo_1058.html
  2428. {
  2429. m_info.m_hasGammaWrites = false;
  2430. }
  2431. if (m_info.m_atiR5xx)
  2432. {
  2433. m_info.m_hasGammaWrites = false; // it just don't, even post 10.6.3
  2434. }
  2435. // if CLI option for fake SRGB mode is enabled, turn off this cap, act like we do not have EXT FB SRGB
  2436. if (CommandLine()->FindParm("-glmenablefakesrgb"))
  2437. {
  2438. m_info.m_hasGammaWrites = false;
  2439. }
  2440. // extension string *could* be checked, but on 10.6.3 the ext string is not there, but the func *is*
  2441. //-------------------------------------------------------------------
  2442. // mixed attach sizes for FBO
  2443. m_info.m_hasMixedAttachmentSizes = true;
  2444. if ( vers < 0x000A0603 ) // pre 10.6.3, no mixed attach sizes
  2445. {
  2446. m_info.m_hasMixedAttachmentSizes = false;
  2447. }
  2448. else
  2449. {
  2450. if ( !strstr( gl_ext_string, "GL_ARB_framebuffer_object" ) )
  2451. {
  2452. // ARB_framebuffer_object not available
  2453. m_info.m_hasMixedAttachmentSizes = false;
  2454. }
  2455. }
  2456. // also check ext string
  2457. //-------------------------------------------------------------------
  2458. // BGRA vert attribs
  2459. m_info.m_hasBGRA = true;
  2460. if ( vers < 0x000A0603 ) // pre 10.6.3, no BGRA attribs
  2461. {
  2462. m_info.m_hasBGRA = false;
  2463. }
  2464. else
  2465. {
  2466. if ( !strstr( gl_ext_string, "EXT_vertex_array_bgra" ) )
  2467. {
  2468. // EXT_vertex_array_bgra not available
  2469. m_info.m_hasBGRA = false;
  2470. }
  2471. }
  2472. //-------------------------------------------------------------------
  2473. m_info.m_hasNewFullscreenMode = true;
  2474. if ( vers < 0x000A0600 ) // pre 10.6.0, no clever window server full screen mode
  2475. {
  2476. m_info.m_hasNewFullscreenMode = false;
  2477. }
  2478. //-------------------------------------------------------------------
  2479. m_info.m_hasNativeClipVertexMode = true;
  2480. // this one uses a heuristic, and allows overrides in case the heuristic is wrong
  2481. // or someone wants to try a beta driver or something.
  2482. // known bad combinations get turned off here..
  2483. // any ATI hardware...
  2484. // TURNED OFF OS CHECK if (m_info.m_osComboVersion <= 0x000A0603)
  2485. // still believe to be broken in 10.6.4
  2486. {
  2487. if (m_info.m_ati)
  2488. {
  2489. m_info.m_hasNativeClipVertexMode = false;
  2490. }
  2491. }
  2492. // R500, forever..
  2493. if (m_info.m_atiR5xx)
  2494. {
  2495. m_info.m_hasNativeClipVertexMode = false;
  2496. }
  2497. // if user disabled them
  2498. if (CommandLine()->FindParm("-glmdisableclipplanes"))
  2499. {
  2500. m_info.m_hasNativeClipVertexMode = false;
  2501. }
  2502. // or maybe enabled them..
  2503. if (CommandLine()->FindParm("-glmenableclipplanes"))
  2504. {
  2505. m_info.m_hasNativeClipVertexMode = true;
  2506. }
  2507. //-------------------------------------------------------------------
  2508. m_info.m_hasOcclusionQuery = true;
  2509. if (!strstr(gl_ext_string, "ARB_occlusion_query"))
  2510. {
  2511. m_info.m_hasOcclusionQuery = false; // you don't got it!
  2512. }
  2513. //-------------------------------------------------------------------
  2514. m_info.m_hasFramebufferBlit = true;
  2515. if (!strstr(gl_ext_string, "EXT_framebuffer_blit"))
  2516. {
  2517. m_info.m_hasFramebufferBlit = false; // you know you don't got it!
  2518. }
  2519. //-------------------------------------------------------------------
  2520. glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_info.m_maxAniso );
  2521. //-------------------------------------------------------------------
  2522. m_info.m_hasBindableUniforms = true;
  2523. if (!strstr(gl_ext_string, "EXT_bindable_uniform"))
  2524. {
  2525. m_info.m_hasBindableUniforms = false;
  2526. }
  2527. m_info.m_hasBindableUniforms = false; // hardwiring this path to false until we see how to accelerate it properly
  2528. //-------------------------------------------------------------------
  2529. m_info.m_hasUniformBuffers = true;
  2530. if (!strstr(gl_ext_string, "ARB_uniform_buffer"))
  2531. {
  2532. m_info.m_hasUniformBuffers = false;
  2533. }
  2534. //-------------------------------------------------------------------
  2535. // test for performance pack (10.6.4+)
  2536. bool perfPackageDetected = GLMDetectSLGU();
  2537. if (perfPackageDetected)
  2538. {
  2539. m_info.m_hasPerfPackage1 = true;
  2540. }
  2541. if (CommandLine()->FindParm("-glmenableperfpackage")) // force it on
  2542. {
  2543. m_info.m_hasPerfPackage1 = true;
  2544. }
  2545. if (CommandLine()->FindParm("-glmdisableperfpackage")) // force it off
  2546. {
  2547. m_info.m_hasPerfPackage1 = false;
  2548. }
  2549. //-------------------------------------------------------------------
  2550. // runtime options that aren't negotiable once set
  2551. m_info.m_hasDualShaders = CommandLine()->FindParm("-glmdualshaders");
  2552. //-------------------------------------------------------------------
  2553. // "can'ts "
  2554. m_info.m_cantBlitReliably = (m_info.m_osComboVersion < 0x000A0606) && m_info.m_intel; //don't trust FBO blit on Intel before 10.6.6
  2555. if (CommandLine()->FindParm("-glmenabletrustblit"))
  2556. {
  2557. m_info.m_cantBlitReliably = false; // we trust the blit, so set the cant-blit cap to false
  2558. }
  2559. if (CommandLine()->FindParm("-glmdisabletrustblit"))
  2560. {
  2561. m_info.m_cantBlitReliably = true; // we do not trust the blit, so set the cant-blit cap to true
  2562. }
  2563. //m_info.m_cantAttachSRGB = (m_info.m_nv && m_info.m_osComboVersion < 0x000A0600); //NV drivers won't accept SRGB tex on an FBO color target in 10.5.8
  2564. //m_info.m_cantAttachSRGB = (m_info.m_ati && m_info.m_osComboVersion < 0x000A0600); //... does ATI have the same problem?
  2565. m_info.m_cantAttachSRGB = (m_info.m_osComboVersion < 0x000A0600); // across the board on 10.5.x actually..
  2566. // MSAA resolve issues
  2567. m_info.m_cantResolveFlipped = false; // initial stance
  2568. if (m_info.m_ati)
  2569. {
  2570. //Jan 2011 - ATI says it's better to do two step blit than to try and resolve upside down
  2571. m_info.m_cantResolveFlipped = true;
  2572. }
  2573. if (m_info.m_nv)
  2574. {
  2575. // we're going to mark it 'broken' unless perf package 1 (10.6.4+) is present
  2576. if (!m_info.m_hasPerfPackage1)
  2577. {
  2578. m_info.m_cantResolveFlipped = true;
  2579. }
  2580. }
  2581. // this is just the private assessment of whather scaled resolve is available.
  2582. // the activation of it will stay tied to the gl_minify_resolve_mode / gl_magnify_resolve_mode convars in glmgr
  2583. bool scaledResolveDetected = nsglCtx && GLMDetectScaledResolveMode( m_info.m_osComboVersion, m_info.m_hasPerfPackage1 );
  2584. m_info.m_cantResolveScaled = !scaledResolveDetected;
  2585. // and you can force it to be "available" if you really want to..
  2586. if ( CommandLine()->FindParm("-gl_force_enable_scaled_resolve") )
  2587. {
  2588. m_info.m_cantResolveScaled = false;
  2589. }
  2590. if ( CommandLine()->FindParm("-gl_force_disable_scaled_resolve") )
  2591. {
  2592. m_info.m_cantResolveScaled = true;
  2593. }
  2594. // gamma decode impacting shader codegen
  2595. m_info.m_costlyGammaFlips = false;
  2596. if (m_info.m_osComboVersion < 0x000A0600) // if Leopard
  2597. m_info.m_costlyGammaFlips = true;
  2598. if (m_info.m_atiR5xx) // or r5xx - always
  2599. m_info.m_costlyGammaFlips = true;
  2600. if ( (m_info.m_atiR6xx) && (m_info.m_osComboVersion < 0x000A0605) ) // or r6xx prior to 10.6.5
  2601. m_info.m_costlyGammaFlips = true;
  2602. [nsglCtx release];
  2603. [pixFmt release];
  2604. [tempPool release];
  2605. }
  2606. GLMRendererInfo::~GLMRendererInfo( void )
  2607. {
  2608. if (m_displays)
  2609. {
  2610. // delete all the new'd renderer infos that the table tracks
  2611. FOR_EACH_VEC( *m_displays, i )
  2612. {
  2613. delete (*this->m_displays)[i];
  2614. }
  2615. delete m_displays;
  2616. m_displays = NULL;
  2617. }
  2618. }
  2619. extern "C" int DisplayInfoSortFunction( GLMDisplayInfo* const *A, GLMDisplayInfo* const *B )
  2620. {
  2621. int bigger = -1;
  2622. int smaller = 1; // adjust these to get the ordering you want
  2623. // check main-ness - main should win
  2624. int mainDisplayID = CGMainDisplayID();
  2625. int mainscreena = (*A)->m_info.m_cgDisplayID == mainDisplayID;
  2626. int mainscreenb = (*B)->m_info.m_cgDisplayID == mainDisplayID;
  2627. if ( mainscreena > mainscreenb )
  2628. {
  2629. return bigger;
  2630. }
  2631. else if ( mainscreena < mainscreenb )
  2632. {
  2633. return smaller;
  2634. }
  2635. // check area - larger screen should win
  2636. int areaa = (*A)->m_info.m_displayPixelWidth * (*A)->m_info.m_displayPixelHeight;
  2637. int areab = (*B)->m_info.m_displayPixelWidth * (*B)->m_info.m_displayPixelHeight;
  2638. if ( areaa > areab )
  2639. {
  2640. return bigger;
  2641. }
  2642. else if ( areaa < areab )
  2643. {
  2644. return smaller;
  2645. }
  2646. return 0; // equal rank
  2647. }
  2648. void GLMRendererInfo::PopulateDisplays( void )
  2649. {
  2650. Assert( !m_displays );
  2651. m_displays = new CUtlVector< GLMDisplayInfo* >;
  2652. for( int i=0; i<32; i++)
  2653. {
  2654. // check mask to see if the selected display intersects this renderer
  2655. CGOpenGLDisplayMask dspMask = (CGOpenGLDisplayMask)(1<<i);
  2656. if ( m_info.m_displayMask & dspMask )
  2657. {
  2658. // exclude teeny displays (they may represent offline displays)
  2659. // exclude inactive displays
  2660. CGDirectDisplayID cgid = CGOpenGLDisplayMaskToDisplayID ( dspMask );
  2661. if ( (cgid != kCGNullDirectDisplay) && CGDisplayIsActive( cgid ) && (CGDisplayPixelsWide( cgid ) >= 512) && (CGDisplayPixelsHigh( cgid ) >= 384) )
  2662. {
  2663. GLMDisplayInfo *newdisp = new GLMDisplayInfo( cgid, dspMask );
  2664. m_displays->AddToTail( newdisp );
  2665. }
  2666. }
  2667. }
  2668. // now sort the table of displays.
  2669. m_displays->Sort( DisplayInfoSortFunction );
  2670. // then go back and ask each display to populate its display mode table.
  2671. FOR_EACH_VEC( *m_displays, i )
  2672. {
  2673. (*this->m_displays)[i]->PopulateModes();
  2674. }
  2675. }
  2676. const char *CheesyRendererDecode( uint value )
  2677. {
  2678. switch(value)
  2679. {
  2680. case 0x00020200 : return "Generic";
  2681. case 0x00020400 : return "GenericFloat";
  2682. case 0x00020600 : return "AppleSW";
  2683. case 0x00021000 : return "ATIRage128";
  2684. case 0x00021200 : return "ATIRadeon";
  2685. case 0x00021400 : return "ATIRagePro";
  2686. case 0x00021600 : return "ATIRadeon8500";
  2687. case 0x00021800 : return "ATIRadeon9700";
  2688. case 0x00021900 : return "ATIRadeonX1000";
  2689. case 0x00021A00 : return "ATIRadeonX2000";
  2690. case 0x00022000 : return "NVGeForce2MX";
  2691. case 0x00022200 : return "NVGeForce3";
  2692. case 0x00022400 : return "NVGeForceFX";
  2693. case 0x00022600 : return "NVGeForce8xxx";
  2694. case 0x00023000 : return "VTBladeXP2";
  2695. case 0x00024000 : return "Intel900";
  2696. case 0x00024200 : return "IntelX3100";
  2697. case 0x00040000 : return "Mesa3DFX";
  2698. default: return "UNKNOWN";
  2699. }
  2700. }
  2701. extern const char *GLMDecode( GLMThing_t thingtype, unsigned long value );
  2702. void GLMRendererInfo::Dump( int which )
  2703. {
  2704. GLMPRINTF(("\n #%d: GLMRendererInfo @ %08x, renderer-id=%s(%08x) display-mask=%08x vram=%dMB",
  2705. which, this,
  2706. CheesyRendererDecode( m_info.m_rendererID & 0x00FFFF00 ), m_info.m_rendererID,
  2707. m_info.m_displayMask,
  2708. m_info.m_vidMemory >> 20
  2709. ));
  2710. GLMPRINTF(("\n VendorID=%04x DeviceID=%04x Model=%s",
  2711. m_info.m_pciVendorID,
  2712. m_info.m_pciDeviceID,
  2713. m_info.m_pciModelString
  2714. ));
  2715. FOR_EACH_VEC( *m_displays, i )
  2716. {
  2717. (*m_displays)[i]->Dump(i);
  2718. }
  2719. }
  2720. //===============================================================================
  2721. GLMDisplayDB::GLMDisplayDB ( void )
  2722. {
  2723. m_renderers = NULL;
  2724. }
  2725. GLMDisplayDB::~GLMDisplayDB ( void )
  2726. {
  2727. if (m_renderers)
  2728. {
  2729. // delete all the new'd renderer infos that the table tracks
  2730. FOR_EACH_VEC( *m_renderers, i )
  2731. {
  2732. delete (*this->m_renderers)[i];
  2733. }
  2734. delete m_renderers;
  2735. m_renderers = NULL;
  2736. }
  2737. }
  2738. extern "C" int RendererInfoSortFunction( GLMRendererInfo * const *A, GLMRendererInfo* const *B )
  2739. {
  2740. int bigger = -1;
  2741. int smaller = 1;
  2742. // check VRAM
  2743. if ( (*A)->m_info.m_vidMemory > (*B)->m_info.m_vidMemory )
  2744. {
  2745. return bigger;
  2746. }
  2747. else if ( (*A)->m_info.m_vidMemory < (*B)->m_info.m_vidMemory )
  2748. {
  2749. return smaller;
  2750. }
  2751. // check MSAA limit
  2752. if ( (*A)->m_info.m_maxSamples > (*B)->m_info.m_maxSamples )
  2753. {
  2754. return bigger;
  2755. }
  2756. else if ( (*A)->m_info.m_maxSamples < (*B)->m_info.m_maxSamples )
  2757. {
  2758. return smaller;
  2759. }
  2760. // prefer discrete devices over Intel integrated
  2761. if ( !(*A)->m_info.m_intel && (*B)->m_info.m_intel )
  2762. {
  2763. return bigger;
  2764. }
  2765. else if ( (*A)->m_info.m_intel && !(*B)->m_info.m_intel )
  2766. {
  2767. return smaller;
  2768. }
  2769. /*
  2770. // this was not a great idea here..
  2771. // check if one has the main screen - is that index 0 in all cases?
  2772. uint maskOfMainDisplay = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() );
  2773. Assert( maskOfMainDisplay==1 ); // just curious
  2774. int mainscreena = (*A)->m_info.m_displayMask & maskOfMainDisplay;
  2775. int mainscreenb = (*B)->m_info.m_displayMask & maskOfMainDisplay;
  2776. if ( mainscreena > mainscreenb )
  2777. {
  2778. return bigger;
  2779. }
  2780. else if ( mainscreena < mainscreenb )
  2781. {
  2782. return smaller;
  2783. }
  2784. */
  2785. return 0; // equal rank
  2786. }
  2787. /** some code that NV gave us. more generalized approach below..
  2788. static io_registry_entry_t lookup_dev_NV(char *name)
  2789. {
  2790. mach_port_t master_port = 0;
  2791. io_iterator_t iterator;
  2792. io_registry_entry_t nub = 0;
  2793. kern_return_t ret;
  2794. IOMasterPort(MACH_PORT_NULL, &master_port);
  2795. ret = IOServiceGetMatchingServices(master_port, IOServiceMatching(name), &iterator);
  2796. if (iterator) {
  2797. nub = IOIteratorNext(iterator);
  2798. if (IOIteratorNext(iterator)) {
  2799. printf("warning: more than one card?\n");
  2800. }
  2801. IOObjectRelease(iterator);
  2802. }
  2803. IOObjectRelease(master_port);
  2804. return nub;
  2805. }
  2806. void GetDriverInfoString_NV( char *driverNameBuf, int driverNameBufLen )
  2807. {
  2808. // courtesy NVIDIA dev rel
  2809. io_registry_entry_t registry;
  2810. kern_return_t ret;
  2811. //
  2812. // Get NVKernel / IOGLBundleName
  2813. //
  2814. registry = lookup_dev_NV("NVKernel");
  2815. if (!registry) {
  2816. fprintf(stderr, "error: could not find NVKernel IORegistry entry!\n");
  2817. return;
  2818. }
  2819. CFMutableDictionaryRef entry;
  2820. ret = IORegistryEntryCreateCFProperties(registry, &entry, kCFAllocatorDefault, 0);
  2821. if (ret != kIOReturnSuccess) {
  2822. fprintf(stderr, "error: could not create CFProperties dictionary!\n");
  2823. return;
  2824. }
  2825. CFStringRef bundle_name_ref = (CFStringRef) CFDictionaryGetValue(entry, CFSTR("IOGLBundleName"));
  2826. if (!bundle_name_ref) {
  2827. fprintf(stderr, "error: could not get IOGLBundleName reference!\n");
  2828. return;
  2829. }
  2830. const char *bundle_name = CFStringGetCStringPtr(bundle_name_ref, CFStringGetSystemEncoding());
  2831. if (!bundle_name) {
  2832. fprintf(stderr, "error: could not get IOGLBundleName!\n");
  2833. return;
  2834. }
  2835. CFStringRef identifier = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.apple.%s"), bundle_name);
  2836. //
  2837. // Get bundle information
  2838. //
  2839. CFBundleRef bundle;
  2840. bundle = CFBundleGetBundleWithIdentifier(identifier);
  2841. if (!bundle) {
  2842. fprintf(stderr, "error: could not get GL driver bundle!\n");
  2843. return;
  2844. }
  2845. CFDictionaryRef dict;
  2846. CFStringRef info;
  2847. dict = CFBundleGetInfoDictionary(bundle);
  2848. if (!dict) {
  2849. fprintf(stderr, "error: could not get bundle info dictionary!\n");
  2850. return;
  2851. }
  2852. info = (CFStringRef) CFDictionaryGetValue(dict, CFSTR("CFBundleGetInfoString"));
  2853. if (!info) {
  2854. fprintf(stderr, "error: could not get CFBundleGetInfoString!\n");
  2855. return;
  2856. }
  2857. CFStringGetCString(info, driverNameBuf, driverNameBufLen, CFStringGetSystemEncoding());
  2858. IOObjectRelease(registry);
  2859. }
  2860. **/
  2861. void GLMDisplayDB::PopulateRenderers( void )
  2862. {
  2863. Assert( !m_renderers );
  2864. m_renderers = new CUtlVector< GLMRendererInfo* >;
  2865. // now walk the renderer list
  2866. // find the eligible ones and insert them into vector
  2867. // if more than one, sort the vector by desirability with favorite at 0
  2868. // then ask each renderer object to populate its displays
  2869. // turns out how you have to do this is to walk the display mask 1<<n..
  2870. // and query at each one, what renderers can hit that one.
  2871. // when you find one, see if it's already in the vector above. if not, add it.
  2872. // later, we sort them.
  2873. for( int i=0; i<32; i++ )
  2874. {
  2875. CGLError cgl_err = (CGLError)0;
  2876. CGLRendererInfoObj cgl_rend = NULL;
  2877. GLint nrend;
  2878. CGOpenGLDisplayMask dspMask = (CGOpenGLDisplayMask)(1<<i);
  2879. CGDirectDisplayID cgid = CGOpenGLDisplayMaskToDisplayID( dspMask );
  2880. bool selected = true; // assume the best
  2881. if (selected)
  2882. {
  2883. if ( (cgid == kCGNullDirectDisplay) || (!CGDisplayIsActive( cgid )) )
  2884. {
  2885. selected = false;
  2886. }
  2887. }
  2888. if (selected)
  2889. {
  2890. cgl_err = CGLQueryRendererInfo( dspMask, &cgl_rend, &nrend ); // FIXME this call spams the console if you ask about an out of bounds display mask
  2891. // "<Error>: unknown error code: invalid display"
  2892. // we can fix that by getting the active display mask first.
  2893. if (!cgl_err)
  2894. {
  2895. // walk the renderers that can hit this display
  2896. // add to table if not already in table, and minimums met
  2897. for( int j=0; j<nrend; j++)
  2898. {
  2899. int problems = 0;
  2900. GLMRendererInfoFields fields;
  2901. memset( &fields, 0, sizeof(fields) );
  2902. // early out if renderer ID already in the table
  2903. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPRendererID, &fields.m_rendererID ); problems += (cgl_err != 0);
  2904. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPDisplayMask, &fields.m_displayMask ); problems += (cgl_err != 0);
  2905. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPFullScreen, &fields.m_fullscreen ); problems += (cgl_err != 0);
  2906. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPAccelerated, &fields.m_accelerated ); problems += (cgl_err != 0);
  2907. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPWindow, &fields.m_windowed ); problems += (cgl_err != 0);
  2908. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPBufferModes, &fields.m_bufferModes ); problems += (cgl_err != 0);
  2909. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPColorModes, &fields.m_colorModes ); problems += (cgl_err != 0);
  2910. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPDepthModes, &fields.m_depthModes ); problems += (cgl_err != 0);
  2911. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPStencilModes, &fields.m_stencilModes ); problems += (cgl_err != 0);
  2912. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxAuxBuffers, &fields.m_maxAuxBuffers ); problems += (cgl_err != 0);
  2913. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxSampleBuffers, &fields.m_maxSampleBuffers ); problems += (cgl_err != 0);
  2914. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxSamples, &fields.m_maxSamples ); problems += (cgl_err != 0);
  2915. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPSampleModes, &fields.m_sampleModes ); problems += (cgl_err != 0);
  2916. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPSampleAlpha, &fields.m_sampleAlpha ); problems += (cgl_err != 0);
  2917. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPVideoMemory, &fields.m_vidMemory ); problems += (cgl_err != 0);
  2918. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPTextureMemory, &fields.m_texMemory ); problems += (cgl_err != 0);
  2919. // decide if this renderer goes in the table.
  2920. // only insert renderers with at least one active display.
  2921. bool selected = !problems;
  2922. if (selected)
  2923. {
  2924. // grab the OS version
  2925. SInt32 vMajor = 0,
  2926. vMinor = 0,
  2927. vMinorMinor = 0;
  2928. OSStatus gestalt_err = 0;
  2929. gestalt_err = Gestalt(gestaltSystemVersionMajor, &vMajor);
  2930. Assert(!gestalt_err);
  2931. gestalt_err = Gestalt(gestaltSystemVersionMinor, &vMinor);
  2932. Assert(!gestalt_err);
  2933. gestalt_err = Gestalt(gestaltSystemVersionBugFix, &vMinorMinor);
  2934. Assert(!gestalt_err);
  2935. //encode into one quantity - 10.6.3 becomes 0x000A0603
  2936. fields.m_osComboVersion = (vMajor << 16) | (vMinor << 8) | (vMinorMinor);
  2937. if (CommandLine()->FindParm("-fakeleopard"))
  2938. {
  2939. // lie
  2940. fields.m_osComboVersion = 0x000A0508;
  2941. }
  2942. if (fields.m_osComboVersion < 0x000A0508)
  2943. {
  2944. // no support below 10.5.8
  2945. // we'll wind up with no valid renderers and give up
  2946. selected = false;
  2947. }
  2948. }
  2949. if (selected)
  2950. {
  2951. // gather more info from IOKit
  2952. // cribbed from http://developer.apple.com/mac/library/samplecode/VideoHardwareInfo/listing3.html
  2953. CFTypeRef typeCode;
  2954. CFDataRef vendorID, deviceID, model;
  2955. io_registry_entry_t dspPort;
  2956. // Get the I/O Kit service port for the display
  2957. dspPort = CGDisplayIOServicePort( cgid );
  2958. // Get the information for the device
  2959. // The vendor ID, device ID, and model are all available as properties of the hardware's I/O Kit service port
  2960. vendorID = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("vendor-id"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
  2961. deviceID = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("device-id"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
  2962. model = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("model"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
  2963. // Send the appropriate data to the outputs checking to validate the data
  2964. if(vendorID)
  2965. {
  2966. fields.m_pciVendorID = *((UInt32*)CFDataGetBytePtr(vendorID));
  2967. }
  2968. else
  2969. {
  2970. fields.m_pciVendorID = 0;
  2971. }
  2972. if(deviceID)
  2973. {
  2974. fields.m_pciDeviceID = *((UInt32*)CFDataGetBytePtr(deviceID));
  2975. }
  2976. else
  2977. {
  2978. fields.m_pciDeviceID = 0;
  2979. }
  2980. if(model)
  2981. {
  2982. int length = CFDataGetLength(model);
  2983. char *data = (char*)CFDataGetBytePtr(model);
  2984. Q_strncpy( fields.m_pciModelString, data, sizeof(fields.m_pciModelString) );
  2985. }
  2986. else
  2987. {
  2988. Q_strncpy( fields.m_pciModelString, "UnknownModel", sizeof(fields.m_pciModelString) );
  2989. }
  2990. // iterate through IOAccelerators til we find one that matches the vendorid and deviceid of this renderer (ugh!)
  2991. // this provides the driver version string which can in turn be used to uniquely identify bad drivers and special case for them
  2992. // first example to date - forcing vsync on 10.6.4 + NV
  2993. {
  2994. io_iterator_t ioIterator = (io_iterator_t)0;
  2995. io_service_t ioAccelerator;
  2996. kern_return_t ioResult = 0;
  2997. bool ioDone = false;
  2998. ioResult = IOServiceGetMatchingServices( kIOMasterPortDefault, IOServiceMatching("IOAccelerator"), &ioIterator );
  2999. if( ioResult == KERN_SUCCESS )
  3000. {
  3001. ioAccelerator = 0;
  3002. while( ( !ioDone ) && ( ioAccelerator = IOIteratorNext( ioIterator ) ) )
  3003. {
  3004. io_service_t ioDevice;
  3005. ioDevice = 0;
  3006. ioResult = IORegistryEntryGetParentEntry( ioAccelerator, kIOServicePlane, &ioDevice);
  3007. CFDataRef this_vendorID, this_deviceID;
  3008. if(ioResult == KERN_SUCCESS)
  3009. {
  3010. this_vendorID = (CFDataRef)IORegistryEntryCreateCFProperty(ioDevice, CFSTR("vendor-id"), kCFAllocatorDefault, kNilOptions );
  3011. this_deviceID = (CFDataRef)IORegistryEntryCreateCFProperty(ioDevice, CFSTR("device-id"), kCFAllocatorDefault, kNilOptions );
  3012. if (this_vendorID && this_deviceID) // null check..
  3013. {
  3014. // see if it matches. if so, do our business (get the extended version string), set ioDone, call it a day
  3015. unsigned short this_vendorIDValue = *(unsigned short*)CFDataGetBytePtr(this_vendorID);
  3016. unsigned short this_deviceIDValue = *(unsigned short*)CFDataGetBytePtr(this_deviceID);
  3017. if ( (fields.m_pciVendorID == this_vendorIDValue) && (fields.m_pciDeviceID == this_deviceIDValue) )
  3018. {
  3019. // see if it matches. if so, do our business (get the extended version string), set ioDone, call it a day
  3020. unsigned short* this_vendorIDBytes = (unsigned short*)CFDataGetBytePtr( this_vendorID );
  3021. unsigned short* this_deviceIDBytes = (unsigned short*)CFDataGetBytePtr( this_deviceID );
  3022. if (this_vendorIDBytes && this_deviceIDBytes) // null check...
  3023. {
  3024. unsigned short this_vendorIDValue = *this_vendorIDBytes;
  3025. unsigned short this_deviceIDValue = *this_deviceIDBytes;
  3026. if ( (fields.m_pciVendorID == this_vendorIDValue) && (fields.m_pciDeviceID == this_deviceIDValue) )
  3027. {
  3028. // match, stop looking
  3029. ioDone = true;
  3030. // get extended info
  3031. CFStringRef this_ioglName = (CFStringRef)IORegistryEntryCreateCFProperty( ioAccelerator, CFSTR("IOGLBundleName"), kCFAllocatorDefault, kNilOptions );
  3032. NSString *bundlePath = [ NSString stringWithFormat:@"/System/Library/Extensions/%@.bundle", this_ioglName ];
  3033. NSDictionary* this_driverDict = [ [NSBundle bundleWithPath: bundlePath] infoDictionary ];
  3034. if (this_driverDict)
  3035. {
  3036. NSString* this_driverInfo = [ this_driverDict objectForKey:@"CFBundleGetInfoString" ];
  3037. if ( this_driverInfo )
  3038. {
  3039. const char* theString = [ this_driverInfo UTF8String ];
  3040. strncpy(fields.m_driverInfoString, theString, sizeof( fields.m_driverInfoString ) );
  3041. }
  3042. }
  3043. // [bundlePath release];
  3044. CFRelease(this_ioglName);
  3045. }
  3046. }
  3047. CFRelease(this_vendorID);
  3048. CFRelease(this_deviceID);
  3049. }
  3050. }
  3051. }
  3052. }
  3053. }
  3054. IOObjectRelease(ioAccelerator);
  3055. IOObjectRelease(ioIterator);
  3056. }
  3057. // Release vendorID, deviceID, and model as appropriate
  3058. if(vendorID)
  3059. CFRelease(vendorID);
  3060. if(deviceID)
  3061. CFRelease(deviceID);
  3062. if(model)
  3063. CFRelease(model);
  3064. // generate shorthand bools
  3065. switch( fields.m_pciVendorID )
  3066. {
  3067. case 0x1002: //ATI
  3068. {
  3069. fields.m_ati = true;
  3070. // http://www.pcidatabase.com/search.php?device_search_str=radeon&device_search.x=0&device_search.y=0&device_search=search+devices
  3071. // Mac-relevant ATI R5xx PCI device ID's lie in this range: 0x7100 - 0x72FF
  3072. // X1600, X1900, X1950
  3073. if ( (fields.m_pciDeviceID >= 0x7100) && (fields.m_pciDeviceID <= 0x72ff) )
  3074. {
  3075. fields.m_atiR5xx = true;
  3076. }
  3077. // R6xx PCI device ID's lie in these ranges:
  3078. // 0x94C1 - 0x9515 ... also 0x9581 - 0x9713
  3079. // 2400HD, 2600HD, 3870, et al
  3080. if (
  3081. ( (fields.m_pciDeviceID >= 0x94C1) && (fields.m_pciDeviceID <= 0x9515) )
  3082. || ( (fields.m_pciDeviceID >= 0x9581) && (fields.m_pciDeviceID <= 0x9713) )
  3083. )
  3084. {
  3085. fields.m_atiR6xx = true;
  3086. }
  3087. // R7xx PCI device ID's lie in: 0x9440 - 0x9460, also 9480-94b5.
  3088. // why there is an HD5000 at 9462, I dunno. Don't think that's an R8xx part.
  3089. if (
  3090. ( (fields.m_pciDeviceID >= 0x9440) && (fields.m_pciDeviceID <= 0x9460) )
  3091. || ( (fields.m_pciDeviceID >= 0x9480) && (fields.m_pciDeviceID <= 0x94B5) )
  3092. )
  3093. {
  3094. fields.m_atiR7xx = true;
  3095. }
  3096. // R8xx: 0x6898-0x68BE
  3097. if ( (fields.m_pciDeviceID >= 0x6898) && (fields.m_pciDeviceID <= 0x68Be) )
  3098. {
  3099. fields.m_atiR8xx = true;
  3100. }
  3101. #if 0
  3102. // turned off, but we could use this for cross check.
  3103. // we could also use the bit encoding of the renderer ID to ferret out a geberation clue.
  3104. // string-scan for each generation
  3105. // this could be a lot better if we got the precise PCI ID's used and/or cross-ref'd that against the driver name
  3106. if (strstr("X1600", fields.m_pciModelString) || strstr("X1900", fields.m_pciModelString) || strstr("X1950", fields.m_pciModelString) )
  3107. {
  3108. fields.m_atiR5xx = true;
  3109. }
  3110. if (strstr("2600", fields.m_pciModelString) || strstr("3870", fields.m_pciModelString) || strstr("X2000", fields.m_pciModelString) )
  3111. {
  3112. fields.m_atiR6xx = true;
  3113. }
  3114. if (strstr("4670", fields.m_pciModelString) || strstr("4650", fields.m_pciModelString) || strstr("4850", fields.m_pciModelString)|| strstr("4870", fields.m_pciModelString) )
  3115. {
  3116. fields.m_atiR7xx = true;
  3117. }
  3118. #endif
  3119. }
  3120. break;
  3121. case 0x8086: //INTC
  3122. {
  3123. fields.m_intel = true;
  3124. switch( fields.m_pciDeviceID )
  3125. {
  3126. case 0x27A6: fields.m_intel95x = true; break; // GMA 950
  3127. case 0x2A02: fields.m_intel3100 = true; break; // X3100
  3128. default:
  3129. {
  3130. if (fields.m_pciDeviceID > 0x2A02) // assume ascending ID's for newer devices
  3131. {
  3132. fields.m_intelHD4000 = true;
  3133. }
  3134. }
  3135. }
  3136. }
  3137. break;
  3138. case 0x10DE: //NV
  3139. {
  3140. fields.m_nv = true;
  3141. // G7x: 0x0391 0x393 0x0395 (7300/7600 GT) 0x009D (Quadro FX)
  3142. if ( (fields.m_pciDeviceID == 0x0391) || (fields.m_pciDeviceID == 0x0393) || (fields.m_pciDeviceID == 0x0395) || (fields.m_pciDeviceID == 0x009D) )
  3143. {
  3144. fields.m_nvG7x = true;
  3145. }
  3146. // G8x: 0400-04ff, also 0x5E1 (GTX280) through 0x08FF
  3147. if (
  3148. ( (fields.m_pciDeviceID >= 0x0400) && (fields.m_pciDeviceID <= 0x04ff) )
  3149. || ( (fields.m_pciDeviceID >= 0x05E1) && (fields.m_pciDeviceID <= 0x08ff) )
  3150. )
  3151. {
  3152. fields.m_nvG8x = true;
  3153. }
  3154. if ( fields.m_pciDeviceID > 0x0900 )
  3155. {
  3156. fields.m_nvNewer = true;
  3157. }
  3158. // detect the specific revision of NV driver in 10.6.4 that caused all the grief
  3159. if (strstr(fields.m_driverInfoString, "1.6.16.11 (19.5.8f01)"))
  3160. {
  3161. fields.m_badDriver1064NV = true;
  3162. }
  3163. }
  3164. break;
  3165. }
  3166. }
  3167. if (selected)
  3168. {
  3169. // dupe check
  3170. FOR_EACH_VEC( *m_renderers, i )
  3171. {
  3172. uint rendid = (*m_renderers)[i]->m_info.m_rendererID;
  3173. if ( rendid == fields.m_rendererID )
  3174. {
  3175. // don't add to table, it's a dupe
  3176. selected = false;
  3177. }
  3178. }
  3179. }
  3180. if (selected)
  3181. {
  3182. // criteria check
  3183. if (fields.m_fullscreen==0)
  3184. selected = false;
  3185. if (fields.m_accelerated==0)
  3186. selected = false;
  3187. if (fields.m_windowed==0)
  3188. selected = false;
  3189. }
  3190. // we need something here that will exclude the renderer if it does not have any good displays attached.
  3191. Assert( fields.m_displayMask != 0 );
  3192. if (selected)
  3193. {
  3194. // add to table
  3195. // note this constructor makes a dummy context just long enough to query remaining fields in the m_info.
  3196. GLMRendererInfo *newinfo = new GLMRendererInfo( &fields );
  3197. m_renderers->AddToTail( newinfo );
  3198. }
  3199. }
  3200. if (cgl_rend)
  3201. {
  3202. CGLDestroyRendererInfo( cgl_rend );
  3203. }
  3204. }
  3205. }
  3206. }
  3207. // now sort the table.
  3208. m_renderers->Sort( RendererInfoSortFunction );
  3209. // then go back and ask each renderer to populate its display info table.
  3210. FOR_EACH_VEC( *m_renderers, i )
  3211. {
  3212. (*m_renderers)[i]->PopulateDisplays();
  3213. }
  3214. }
  3215. void GLMDisplayDB::PopulateFakeAdapters( uint realRendererIndex ) // fake adapters = one real adapter times however many displays are on it
  3216. {
  3217. // presumption is that renderers have been populated.
  3218. Assert( GetRendererCount() > 0 );
  3219. Assert( realRendererIndex < GetRendererCount() );
  3220. m_fakeAdapters.RemoveAll();
  3221. // for( int r = 0; r < GetRendererCount(); r++ )
  3222. int r = realRendererIndex;
  3223. {
  3224. for( int d = 0; d < GetDisplayCount( r ); d++ )
  3225. {
  3226. GLMFakeAdapter temp;
  3227. temp.m_rendererIndex = r;
  3228. temp.m_displayIndex = d;
  3229. m_fakeAdapters.AddToTail( temp );
  3230. }
  3231. }
  3232. }
  3233. void GLMDisplayDB::Populate(void)
  3234. {
  3235. this->PopulateRenderers();
  3236. // passing in zero here, constrains the set of fake adapters (GL renderer + a display) to the ones using the highest ranked renderer.
  3237. //FIXME introduce some kind of convar allowing selection of other GPU's in the system.
  3238. int realRendererIndex = 0;
  3239. if (CommandLine()->FindParm("-glmrenderer0"))
  3240. realRendererIndex = 0;
  3241. if (CommandLine()->FindParm("-glmrenderer1"))
  3242. realRendererIndex = 1;
  3243. if (CommandLine()->FindParm("-glmrenderer2"))
  3244. realRendererIndex = 2;
  3245. if (CommandLine()->FindParm("-glmrenderer3"))
  3246. realRendererIndex = 3;
  3247. if (realRendererIndex >= GetRendererCount())
  3248. {
  3249. // fall back to 0
  3250. realRendererIndex = 0;
  3251. }
  3252. this->PopulateFakeAdapters( 0 );
  3253. #if GLMDEBUG
  3254. this->Dump();
  3255. #endif
  3256. }
  3257. int GLMDisplayDB::GetFakeAdapterCount( void )
  3258. {
  3259. return m_fakeAdapters.Count();
  3260. }
  3261. bool GLMDisplayDB::GetFakeAdapterInfo( int fakeAdapterIndex, int *rendererOut, int *displayOut, GLMRendererInfoFields *rendererInfoOut, GLMDisplayInfoFields *displayInfoOut )
  3262. {
  3263. if (fakeAdapterIndex >= GetFakeAdapterCount() )
  3264. {
  3265. *rendererOut = 0;
  3266. *displayOut = 0;
  3267. return true; // fail
  3268. }
  3269. *rendererOut = m_fakeAdapters[fakeAdapterIndex].m_rendererIndex;
  3270. *displayOut = m_fakeAdapters[fakeAdapterIndex].m_displayIndex;
  3271. bool rendResult = GetRendererInfo( *rendererOut, rendererInfoOut );
  3272. bool dispResult = GetDisplayInfo( *rendererOut, *displayOut, displayInfoOut );
  3273. return rendResult || dispResult;
  3274. }
  3275. int GLMDisplayDB::GetRendererCount( void )
  3276. {
  3277. return m_renderers->Count();
  3278. }
  3279. bool GLMDisplayDB::GetRendererInfo( int rendererIndex, GLMRendererInfoFields *infoOut )
  3280. {
  3281. memset( infoOut, 0, sizeof( GLMRendererInfoFields ) );
  3282. if (rendererIndex >= GetRendererCount())
  3283. return true; // fail
  3284. GLMRendererInfo *rendInfo = (*m_renderers)[rendererIndex];
  3285. *infoOut = rendInfo->m_info;
  3286. return false;
  3287. }
  3288. int GLMDisplayDB::GetDisplayCount( int rendererIndex )
  3289. {
  3290. if (rendererIndex >= GetRendererCount())
  3291. return 0; // fail
  3292. GLMRendererInfo *rendInfo = (*m_renderers)[rendererIndex];
  3293. return rendInfo->m_displays->Count();
  3294. }
  3295. bool GLMDisplayDB::GetDisplayInfo( int rendererIndex, int displayIndex, GLMDisplayInfoFields *infoOut )
  3296. {
  3297. memset( infoOut, 0, sizeof( GLMDisplayInfoFields ) );
  3298. if (rendererIndex >= GetRendererCount())
  3299. return true; // fail
  3300. if (displayIndex >= GetDisplayCount(rendererIndex))
  3301. return true; // fail
  3302. GLMDisplayInfo *displayInfo = (*(*m_renderers)[rendererIndex]->m_displays)[displayIndex];
  3303. *infoOut = displayInfo->m_info;
  3304. return false;
  3305. }
  3306. int GLMDisplayDB::GetModeCount( int rendererIndex, int displayIndex )
  3307. {
  3308. if (rendererIndex >= GetRendererCount())
  3309. return 0; // fail
  3310. if (displayIndex >= GetDisplayCount(rendererIndex))
  3311. return 0; // fail
  3312. GLMDisplayInfo *displayInfo = (*(*m_renderers)[rendererIndex]->m_displays)[displayIndex];
  3313. return displayInfo->m_modes->Count();
  3314. }
  3315. bool GLMDisplayDB::GetModeInfo( int rendererIndex, int displayIndex, int modeIndex, GLMDisplayModeInfoFields *infoOut )
  3316. {
  3317. memset( infoOut, 0, sizeof( GLMDisplayModeInfoFields ) );
  3318. if (rendererIndex >= GetRendererCount())
  3319. return true; // fail
  3320. if (displayIndex >= GetDisplayCount(rendererIndex))
  3321. return true; // fail
  3322. if (modeIndex >= GetModeCount(rendererIndex,displayIndex))
  3323. return true; // fail
  3324. if (modeIndex>=0)
  3325. {
  3326. GLMDisplayMode *displayModeInfo = (*(*(*m_renderers)[rendererIndex]->m_displays)[displayIndex]->m_modes)[ modeIndex ];
  3327. *infoOut = displayModeInfo->m_info;
  3328. }
  3329. else
  3330. {
  3331. // passing modeIndex = -1 means "tell me about current mode"..
  3332. GLMRendererInfo *rendInfo = (*m_renderers)[ rendererIndex ];
  3333. GLMDisplayInfo *dispinfo = (*rendInfo ->m_displays)[displayIndex];
  3334. CGDirectDisplayID cgid = dispinfo->m_info.m_cgDisplayID;
  3335. CFDictionaryRef curModeDict = CGDisplayCurrentMode( cgid );
  3336. CFNumberRef number;
  3337. CFBooleanRef boolean;
  3338. CFArrayRef modeList;
  3339. CGDisplayErr cgderr;
  3340. // get the mode number from the mode dict (using system mode numbering, not our sorted numbering)
  3341. if (curModeDict)
  3342. {
  3343. int modeIndex=0;
  3344. number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayMode);
  3345. CFNumberGetValue(number, kCFNumberLongType, &modeIndex);
  3346. // grab the width and height, I am unclear on whether this is the displayed FB width or the display device width.
  3347. int screenWidth=0;
  3348. int screenHeight=0;
  3349. int refreshHz=0;
  3350. number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayWidth);
  3351. CFNumberGetValue(number, kCFNumberIntType, &screenWidth);
  3352. number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayHeight);
  3353. CFNumberGetValue(number, kCFNumberIntType, &screenHeight);
  3354. number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayRefreshRate);
  3355. CFNumberGetValue(number, kCFNumberIntType, &refreshHz);
  3356. GLMPRINTF(( "-D- GLMDisplayDB::GetModeInfo sees mode-index=%d, width=%d, height=%d on CGID %08x (display index %d on rendererindex %d)",
  3357. modeIndex,
  3358. screenWidth,
  3359. screenHeight,
  3360. cgid,
  3361. displayIndex,
  3362. rendererIndex ));
  3363. // now match
  3364. int foundIndex = -1;
  3365. FOR_EACH_VEC( (*dispinfo->m_modes), i )
  3366. {
  3367. GLMDisplayMode *mode = (*dispinfo->m_modes)[i];
  3368. if (mode->m_info.m_modePixelWidth == screenWidth)
  3369. {
  3370. if (mode->m_info.m_modePixelHeight == screenHeight)
  3371. {
  3372. if (mode->m_info.m_modeRefreshHz == refreshHz)
  3373. {
  3374. foundIndex = i;
  3375. *infoOut = mode->m_info;
  3376. return false;
  3377. }
  3378. }
  3379. }
  3380. }
  3381. }
  3382. // if we get here, we could not find the mode
  3383. memset( infoOut, 0, sizeof( *infoOut ) );
  3384. return true; // fail
  3385. }
  3386. return false;
  3387. }
  3388. void GLMDisplayDB::Dump( void )
  3389. {
  3390. GLMPRINTF(("\n GLMDisplayDB @ %08x ",this ));
  3391. FOR_EACH_VEC( *m_renderers, i )
  3392. {
  3393. (*m_renderers)[i]->Dump(i);
  3394. }
  3395. }