├── .gitattributes ├── .gitignore ├── Cocoa.m ├── LICENSE ├── README.md └── Sources.zip /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | bower_components/ 163 | 164 | # RIA/Silverlight projects 165 | Generated_Code/ 166 | 167 | # Backup & report files from converting an old project file 168 | # to a newer Visual Studio version. Backup files are not needed, 169 | # because we have git ;-) 170 | _UpgradeReport_Files/ 171 | Backup*/ 172 | UpgradeLog*.XML 173 | UpgradeLog*.htm 174 | 175 | # SQL Server files 176 | *.mdf 177 | *.ldf 178 | 179 | # Business Intelligence projects 180 | *.rdl.data 181 | *.bim.layout 182 | *.bim_*.settings 183 | 184 | # Microsoft Fakes 185 | FakesAssemblies/ 186 | 187 | # LightSwitch generated files 188 | GeneratedArtifacts/ 189 | _Pvt_Extensions/ 190 | ModelManifest.xml -------------------------------------------------------------------------------- /Cocoa.m: -------------------------------------------------------------------------------- 1 | // gcc Cocoa.m -o OSXWindow -framework Cocoa -framework Quartz -framework OpenGL 2 | 3 | #import 4 | #import 5 | #import 6 | #include 7 | 8 | @class View; 9 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void*); 10 | 11 | @interface View : NSOpenGLView { 12 | @public 13 | CVDisplayLinkRef displayLink; 14 | bool running; 15 | NSRect windowRect; 16 | NSRecursiveLock* appLock; 17 | } 18 | @end 19 | 20 | @implementation View 21 | // Initialize 22 | - (id) initWithFrame: (NSRect) frame { 23 | running = true; 24 | 25 | // No multisampling 26 | int samples = 0; 27 | 28 | // Keep multisampling attributes at the start of the attribute lists since code below assumes they are array elements 0 through 4. 29 | NSOpenGLPixelFormatAttribute windowedAttrs[] = 30 | { 31 | NSOpenGLPFAMultisample, 32 | NSOpenGLPFASampleBuffers, samples ? 1 : 0, 33 | NSOpenGLPFASamples, samples, 34 | NSOpenGLPFAAccelerated, 35 | NSOpenGLPFADoubleBuffer, 36 | NSOpenGLPFAColorSize, 32, 37 | NSOpenGLPFADepthSize, 24, 38 | NSOpenGLPFAAlphaSize, 8, 39 | NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy, 40 | 0 41 | }; 42 | 43 | // Try to choose a supported pixel format 44 | NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowedAttrs]; 45 | 46 | if (!pf) { 47 | bool valid = false; 48 | while (!pf && samples > 0) { 49 | samples /= 2; 50 | windowedAttrs[2] = samples ? 1 : 0; 51 | windowedAttrs[4] = samples; 52 | pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowedAttrs]; 53 | if (pf) { 54 | valid = true; 55 | break; 56 | } 57 | } 58 | 59 | if (!valid) { 60 | NSLog(@"OpenGL pixel format not supported."); 61 | return nil; 62 | } 63 | } 64 | 65 | self = [super initWithFrame:frame pixelFormat:[pf autorelease]]; 66 | appLock = [[NSRecursiveLock alloc] init]; 67 | 68 | return self; 69 | } 70 | 71 | - (void) prepareOpenGL { 72 | [super prepareOpenGL]; 73 | 74 | [[self window] setLevel: NSNormalWindowLevel]; 75 | [[self window] makeKeyAndOrderFront: self]; 76 | 77 | // Make all the OpenGL calls to setup rendering and build the necessary rendering objects 78 | [[self openGLContext] makeCurrentContext]; 79 | // Synchronize buffer swaps with vertical refresh rate 80 | GLint swapInt = 1; // Vsynch on! 81 | [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 82 | 83 | // Create a display link capable of being used with all active displays 84 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); 85 | 86 | // Set the renderer output callback function 87 | CVDisplayLinkSetOutputCallback(displayLink, &GlobalDisplayLinkCallback, self); 88 | 89 | CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj]; 90 | CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj]; 91 | CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); 92 | 93 | GLint dim[2] = {windowRect.size.width, windowRect.size.height}; 94 | CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim); 95 | CGLEnable(cglContext, kCGLCESurfaceBackingSize); 96 | 97 | [appLock lock]; 98 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 99 | NSLog(@"Initialize"); 100 | 101 | NSLog(@"GL version: %s", glGetString(GL_VERSION)); 102 | NSLog(@"GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); 103 | // Temp 104 | glClearColor(0.5f, 0.6f, 0.7f, 1.0f); 105 | glViewport(0, 0, windowRect.size.width, windowRect.size.height); 106 | glEnable(GL_DEPTH_TEST); 107 | // End temp 108 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 109 | [appLock unlock]; 110 | 111 | // Activate the display link 112 | CVDisplayLinkStart(displayLink); 113 | } 114 | 115 | // Tell the window to accept input events 116 | - (BOOL)acceptsFirstResponder { 117 | return YES; 118 | } 119 | 120 | - (void)mouseMoved:(NSEvent*) event { 121 | [appLock lock]; 122 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 123 | NSLog(@"Mouse pos: %lf, %lf", point.x, point.y); 124 | [appLock unlock]; 125 | } 126 | 127 | - (void) mouseDragged: (NSEvent*) event { 128 | [appLock lock]; 129 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 130 | NSLog(@"Mouse pos: %lf, %lf", point.x, point.y); 131 | [appLock unlock]; 132 | } 133 | 134 | - (void)scrollWheel: (NSEvent*) event { 135 | [appLock lock]; 136 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 137 | NSLog(@"Mouse wheel at: %lf, %lf. Delta: %lf", point.x, point.y, [event deltaY]); 138 | [appLock unlock]; 139 | } 140 | 141 | - (void) mouseDown: (NSEvent*) event { 142 | [appLock lock]; 143 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 144 | NSLog(@"Left mouse down: %lf, %lf", point.x, point.y); 145 | [appLock unlock]; 146 | } 147 | 148 | - (void) mouseUp: (NSEvent*) event { 149 | [appLock lock]; 150 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 151 | NSLog(@"Left mouse up: %lf, %lf", point.x, point.y); 152 | [appLock unlock]; 153 | } 154 | 155 | - (void) rightMouseDown: (NSEvent*) event { 156 | [appLock lock]; 157 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 158 | NSLog(@"Right mouse down: %lf, %lf", point.x, point.y); 159 | [appLock unlock]; 160 | } 161 | 162 | - (void) rightMouseUp: (NSEvent*) event { 163 | [appLock lock]; 164 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 165 | NSLog(@"Right mouse up: %lf, %lf", point.x, point.y); 166 | [appLock unlock]; 167 | } 168 | 169 | - (void)otherMouseDown: (NSEvent*) event { 170 | [appLock lock]; 171 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 172 | NSLog(@"Middle mouse down: %lf, %lf", point.x, point.y); 173 | [appLock unlock]; 174 | } 175 | 176 | - (void)otherMouseUp: (NSEvent*) event { 177 | [appLock lock]; 178 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 179 | NSLog(@"Middle mouse up: %lf, %lf", point.x, point.y); 180 | [appLock unlock]; 181 | } 182 | 183 | - (void) mouseEntered: (NSEvent*)event { 184 | [appLock lock]; 185 | NSLog(@"Mouse entered"); 186 | [appLock unlock]; 187 | } 188 | 189 | - (void) mouseExited: (NSEvent*)event { 190 | [appLock lock]; 191 | NSLog(@"Mouse left"); 192 | [appLock unlock]; 193 | } 194 | 195 | - (void) keyDown: (NSEvent*) event { 196 | [appLock lock]; 197 | if ([event isARepeat] == NO) { 198 | NSLog(@"Key down: %d", [event keyCode]); 199 | } 200 | [appLock unlock]; 201 | } 202 | 203 | - (void) keyUp: (NSEvent*) event { 204 | [appLock lock]; 205 | NSLog(@"Key up: %d", [event keyCode]); 206 | [appLock unlock]; 207 | } 208 | 209 | // Update 210 | - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime { 211 | [appLock lock]; 212 | 213 | [[self openGLContext] makeCurrentContext]; 214 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 215 | 216 | NSLog(@"Update"); 217 | // Temp 218 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 219 | 220 | // EndTemp 221 | 222 | CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]); 223 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 224 | 225 | if (false) { // Update loop returns false 226 | [NSApp terminate:self]; 227 | } 228 | 229 | [appLock unlock]; 230 | 231 | return kCVReturnSuccess; 232 | } 233 | 234 | // Resize 235 | - (void)windowDidResize:(NSNotification*)notification { 236 | NSSize size = [ [ _window contentView ] frame ].size; 237 | [appLock lock]; 238 | [[self openGLContext] makeCurrentContext]; 239 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 240 | NSLog(@"Window resize: %lf, %lf", size.width, size.height); 241 | // Temp 242 | windowRect.size.width = size.width; 243 | windowRect.size.height = size.height; 244 | glViewport(0, 0, windowRect.size.width, windowRect.size.height); 245 | // End temp 246 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 247 | [appLock unlock]; 248 | } 249 | 250 | - (void)resumeDisplayRenderer { 251 | [appLock lock]; 252 | CVDisplayLinkStop(displayLink); 253 | [appLock unlock]; 254 | } 255 | 256 | - (void)haltDisplayRenderer { 257 | [appLock lock]; 258 | CVDisplayLinkStop(displayLink); 259 | [appLock unlock]; 260 | } 261 | 262 | // Terminate window when the red X is pressed 263 | -(void)windowWillClose:(NSNotification *)notification { 264 | if (running) { 265 | running = false; 266 | 267 | [appLock lock]; 268 | NSLog(@"Cleanup"); 269 | 270 | CVDisplayLinkStop(displayLink); 271 | CVDisplayLinkRelease(displayLink); 272 | 273 | [appLock unlock]; 274 | } 275 | 276 | [NSApp terminate:self]; 277 | } 278 | 279 | // Cleanup 280 | - (void) dealloc { 281 | [appLock release]; 282 | [super dealloc]; 283 | } 284 | @end 285 | 286 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { 287 | CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime]; 288 | return result; 289 | } 290 | 291 | int main(int argc, const char * argv[]) { 292 | // Autorelease Pool: 293 | // Objects declared in this scope will be automatically 294 | // released at the end of it, when the pool is "drained". 295 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 296 | 297 | // Create a shared app instance. 298 | // This will initialize the global variable 299 | // 'NSApp' with the application instance. 300 | [NSApplication sharedApplication]; 301 | 302 | // Create a window: 303 | 304 | // Style flags 305 | NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask; 306 | 307 | // Window bounds (x, y, width, height) 308 | NSRect screenRect = [[NSScreen mainScreen] frame]; 309 | NSRect viewRect = NSMakeRect(0, 0, 800, 600); 310 | NSRect windowRect = NSMakeRect(NSMidX(screenRect) - NSMidX(viewRect), 311 | NSMidY(screenRect) - NSMidY(viewRect), 312 | viewRect.size.width, 313 | viewRect.size.height); 314 | 315 | NSWindow * window = [[NSWindow alloc] initWithContentRect:windowRect 316 | styleMask:windowStyle 317 | backing:NSBackingStoreBuffered 318 | defer:NO]; 319 | [window autorelease]; 320 | 321 | // Window controller 322 | NSWindowController * windowController = [[NSWindowController alloc] initWithWindow:window]; 323 | [windowController autorelease]; 324 | 325 | // Since Snow Leopard, programs without application bundles and Info.plist files don't get a menubar 326 | // and can't be brought to the front unless the presentation option is changed 327 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 328 | 329 | // Next, we need to create the menu bar. You don't need to give the first item in the menubar a name 330 | // (it will get the application's name automatically) 331 | id menubar = [[NSMenu new] autorelease]; 332 | id appMenuItem = [[NSMenuItem new] autorelease]; 333 | [menubar addItem:appMenuItem]; 334 | [NSApp setMainMenu:menubar]; 335 | 336 | // Then we add the quit item to the menu. Fortunately the action is simple since terminate: is 337 | // already implemented in NSApplication and the NSApplication is always in the responder chain. 338 | id appMenu = [[NSMenu new] autorelease]; 339 | id appName = [[NSProcessInfo processInfo] processName]; 340 | id quitTitle = [@"Quit " stringByAppendingString:appName]; 341 | id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle 342 | action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; 343 | [appMenu addItem:quitMenuItem]; 344 | [appMenuItem setSubmenu:appMenu]; 345 | 346 | // Create app delegate to handle system events 347 | View* view = [[[View alloc] initWithFrame:windowRect] autorelease]; 348 | view->windowRect = windowRect; 349 | [window setAcceptsMouseMovedEvents:YES]; 350 | [window setContentView:view]; 351 | [window setDelegate:view]; 352 | 353 | // Set app title 354 | [window setTitle:appName]; 355 | 356 | // Add fullscreen button 357 | [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; 358 | 359 | // Show window and run event loop 360 | [window orderFrontRegardless]; 361 | [NSApp run]; 362 | 363 | [pool drain]; 364 | 365 | return (0); 366 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | #Part 1, Creating a window 3 | The first thing we need is a __NSAutoreleasePool__, objects declared within it's scope will be automatically released when the pool is "drained". 4 | ``` 5 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 6 | ``` 7 | Next, we create a shared app instance. This will initialize the global variable *NSApp* with the application instance. 8 | ``` 9 | [NSApplication sharedApplication]; 10 | ``` 11 | Then we create the actual __NSWindow__ object. 12 | ``` 13 | NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask; 14 | 15 | NSRect windowRect = NSMakeRect(100, 100, 400, 400); // x, y, w, h 16 | 17 | NSWindow * window = [[NSWindow alloc] initWithContentRect:windowRect 18 | styleMask:windowStyle 19 | backing:NSBackingStoreBuffered 20 | defer:NO]; 21 | 22 | [window autorelease]; 23 | ``` 24 | After the window was created a window controller needs to be created as well 25 | ``` 26 | NSWindowController * windowController = [[NSWindowController alloc] initWithWindow:window]; 27 | [windowController autorelease]; 28 | ``` 29 | Finally we show the window and run the event loop, the pool declared above must be drained at this point 30 | ``` 31 | [window orderFrontRegardless]; 32 | [NSApp run]; 33 | [pool drain] 34 | ``` 35 | ###Sample Code (Objective c) 36 | ``` 37 | #import 38 | 39 | int main(int argc, const char * argv[]) { 40 | // Autorelease Pool: 41 | // Objects declared in this scope will be automatically 42 | // released at the end of it, when the pool is "drained". 43 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 44 | 45 | // Create a shared app instance. 46 | // This will initialize the global variable 47 | // 'NSApp' with the application instance. 48 | [NSApplication sharedApplication]; 49 | 50 | // Create a window: 51 | 52 | // Style flags 53 | NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask; 54 | 55 | // Window bounds (x, y, width, height) 56 | NSRect windowRect = NSMakeRect(100, 100, 400, 400); 57 | 58 | NSWindow * window = [[NSWindow alloc] initWithContentRect:windowRect 59 | styleMask:windowStyle 60 | backing:NSBackingStoreBuffered 61 | defer:NO]; 62 | [window autorelease]; 63 | 64 | // Window controller 65 | NSWindowController * windowController = [[NSWindowController alloc] initWithWindow:window]; 66 | [windowController autorelease]; 67 | 68 | // TODO: Create app delegate to handle system events. 69 | // TODO: Create menus (especially Quit!) 70 | 71 | // Show window and run event loop 72 | [window orderFrontRegardless]; 73 | [NSApp run]; 74 | 75 | [pool drain]; 76 | 77 | return (0); 78 | } 79 | ``` 80 | Compile with 81 | ``` 82 | gcc Cocoa.m -o OSXWindow -framework Cocoa 83 | ``` 84 | #Part 2, Adding a menu 85 | Since Snow Leopard, programs without application bundles and Info.plist files don't get a menubar and can't be brought to the front unless the presentation option is changed 86 | ``` 87 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 88 | ``` 89 | Next, we need to create the menu bar. You don't need to give the first item in the menubar a name (it will get the application's name automatically) 90 | ``` 91 | id menubar = [[NSMenu new] autorelease]; 92 | id appMenuItem = [[NSMenuItem new] autorelease]; 93 | [menubar addItem:appMenuItem]; 94 | [NSApp setMainMenu:menubar]; 95 | ``` 96 | Then we add the quit item to the menu. Fortunately the action is simple since terminate: is already implemented in NSApplication and the NSApplication is always in the responder chain. 97 | ``` 98 | id appMenu = [[NSMenu new] autorelease]; 99 | id appName = [[NSProcessInfo processInfo] processName]; 100 | id quitTitle = [@"Quit " stringByAppendingString:appName]; 101 | id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle 102 | action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; 103 | [appMenu addItem:quitMenuItem]; 104 | [appMenuItem setSubmenu:appMenu]; 105 | ``` 106 | ###Sample Code 107 | ``` 108 | // gcc Cocoa.m -o OSXWindow -framework Cocoa 109 | 110 | #import 111 | 112 | int main(int argc, const char * argv[]) { 113 | // Autorelease Pool: 114 | // Objects declared in this scope will be automatically 115 | // released at the end of it, when the pool is "drained". 116 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 117 | 118 | // Create a shared app instance. 119 | // This will initialize the global variable 120 | // 'NSApp' with the application instance. 121 | [NSApplication sharedApplication]; 122 | 123 | // Create a window: 124 | 125 | // Style flags 126 | NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask; 127 | 128 | // Window bounds (x, y, width, height) 129 | NSRect windowRect = NSMakeRect(100, 100, 400, 400); 130 | 131 | NSWindow * window = [[NSWindow alloc] initWithContentRect:windowRect 132 | styleMask:windowStyle 133 | backing:NSBackingStoreBuffered 134 | defer:NO]; 135 | [window autorelease]; 136 | 137 | // Window controller 138 | NSWindowController * windowController = [[NSWindowController alloc] initWithWindow:window]; 139 | [windowController autorelease]; 140 | 141 | // Since Snow Leopard, programs without application bundles and Info.plist files don't get a menubar 142 | // and can't be brought to the front unless the presentation option is changed 143 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 144 | 145 | // Next, we need to create the menu bar. You don't need to give the first item in the menubar a name 146 | // (it will get the application's name automatically) 147 | id menubar = [[NSMenu new] autorelease]; 148 | id appMenuItem = [[NSMenuItem new] autorelease]; 149 | [menubar addItem:appMenuItem]; 150 | [NSApp setMainMenu:menubar]; 151 | 152 | // Then we add the quit item to the menu. Fortunately the action is simple since terminate: is 153 | // already implemented in NSApplication and the NSApplication is always in the responder chain. 154 | id appMenu = [[NSMenu new] autorelease]; 155 | id appName = [[NSProcessInfo processInfo] processName]; 156 | id quitTitle = [@"Quit " stringByAppendingString:appName]; 157 | id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle 158 | action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; 159 | [appMenu addItem:quitMenuItem]; 160 | [appMenuItem setSubmenu:appMenu]; 161 | 162 | // TODO: Create app delegate to handle system events. 163 | 164 | // Set app title 165 | [window setTitle:appName]; 166 | 167 | // Show window and run event loop 168 | [window orderFrontRegardless]; 169 | [NSApp run]; 170 | 171 | [pool drain]; 172 | 173 | return (0); 174 | } 175 | ``` 176 | #Part 3, Events 177 | Unlike in X11 or Win32 Cocoa does not give us explicit control over the event loop. Instead we must create a delegate, and respond to certain events. Lets define a *View* class, this will be both our event delegate and render area. 178 | ``` 179 | @class View; 180 | 181 | @interface View : NSView { 182 | @public 183 | bool running; 184 | } 185 | @end 186 | ``` 187 | Now lets start the implementation with the "standard" _initWithFrame_ function. 188 | ``` 189 | - (id) initWithFrame: (NSRect) frame { 190 | self = [super initWithFrame:frame]; 191 | running = true; 192 | // TODO: Init OpenGL 193 | NSLog(@"initialize"); 194 | 195 | return self; 196 | } 197 | ``` 198 | In order to respond to mouse and keyboard events we must be the first responder 199 | ``` 200 | - (BOOL)acceptsFirstResponder { 201 | return YES; 202 | } 203 | ``` 204 | We will respond to the following mouse events __mouseMoved__, __mouseDragged__, __scrollWheel__, __mouseDown__, __mouseUp__, __rightMouseDown__, __rightMouseUp__, __otherMouseDown__, __otherMouseUp__, __mouseEntered__ and __mouseExited__. 205 | ``` 206 | - (void)mouseMoved:(NSEvent*) event { 207 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 208 | NSLog(@"Mouse pos: %lf, %lf", point.x, point.y); 209 | } 210 | 211 | - (void) mouseDragged: (NSEvent*) event { 212 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 213 | NSLog(@"Mouse pos: %lf, %lf", point.x, point.y); 214 | } 215 | 216 | - (void)scrollWheel: (NSEvent*) event { 217 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 218 | NSLog(@"Mouse wheel at: %lf, %lf. Delta: %lf", point.x, point.y, [event deltaY] * 10.0); 219 | } 220 | 221 | - (void) mouseDown: (NSEvent*) event { 222 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 223 | NSLog(@"Left mouse down: %lf, %lf", point.x, point.y); 224 | } 225 | 226 | - (void) mouseUp: (NSEvent*) event { 227 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 228 | NSLog(@"Left mouse up: %lf, %lf", point.x, point.y); 229 | } 230 | 231 | - (void) rightMouseDown: (NSEvent*) event { 232 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 233 | NSLog(@"Right mouse down: %lf, %lf", point.x, point.y); 234 | } 235 | 236 | - (void) rightMouseUp: (NSEvent*) event { 237 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 238 | NSLog(@"Right mouse up: %lf, %lf", point.x, point.y); 239 | } 240 | 241 | - (void)otherMouseDown: (NSEvent*) event { 242 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 243 | NSLog(@"Middle mouse down: %lf, %lf", point.x, point.y); 244 | } 245 | 246 | - (void)otherMouseUp: (NSEvent*) event { 247 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 248 | NSLog(@"Middle mouse up: %lf, %lf", point.x, point.y); 249 | } 250 | 251 | - (void) mouseEntered: (NSEvent*)event { 252 | NSLog(@"Mouse entered"); 253 | } 254 | 255 | - (void) mouseExited: (NSEvent*)event { 256 | NSLog(@"Mouse left"); 257 | } 258 | ``` 259 | We will process keyboard events with __keyDown__ and __keyUp__ 260 | ``` 261 | - (void) keyDown: (NSEvent*) event { 262 | if ([event isARepeat] == NO) { 263 | NSLog(@"Key down: %d", [event keyCode]); 264 | } 265 | } 266 | 267 | - (void) keyUp: (NSEvent*) event { 268 | NSLog(@"Key up: %d", [event keyCode]); 269 | } 270 | ``` 271 | Lets also respond to the window being resized 272 | ``` 273 | // Resize 274 | - (void)windowDidResize:(NSNotification*)notification { 275 | NSSize size = [ [ _window contentView ] frame ].size; 276 | NSLog(@"Window resize: %lf, %lf", size.width, size.height); 277 | } 278 | ``` 279 | We will handle cleanup in the __windowWillClose__ function, we also force the app to terminate. This will cause the app to close when teh red X is pressed. Also, implement a stock __dealloc__ function 280 | ``` 281 | -(void)windowWillClose:(NSNotification *)notification { 282 | if (running) { 283 | running = false; 284 | 285 | NSLog(@"Cleanup"); 286 | } 287 | 288 | [NSApp terminate:self]; 289 | } 290 | 291 | - (void) dealloc { 292 | [super dealloc]; 293 | } 294 | ``` 295 | Inside the main function, we create a View, and set it's delegate 296 | ``` 297 | View* view = [[[View alloc] initWithFrame:windowRect] autorelease]; 298 | [window setAcceptsMouseMovedEvents:YES]; 299 | [window setContentView:view]; 300 | [window setDelegate:view]; 301 | ``` 302 | ### Sample Code 303 | ``` 304 | // gcc Cocoa.m -o OSXWindow -framework Cocoa 305 | 306 | #import 307 | 308 | @class View; 309 | 310 | @interface View : NSView { 311 | @public 312 | bool running; 313 | } 314 | @end 315 | 316 | @implementation View 317 | // Initialize 318 | - (id) initWithFrame: (NSRect) frame { 319 | self = [super initWithFrame:frame]; 320 | running = true; 321 | // TODO: Init OpenGL 322 | 323 | NSLog(@"initialize"); 324 | 325 | return self; 326 | } 327 | 328 | // Tell the window to accept input events 329 | - (BOOL)acceptsFirstResponder { 330 | return YES; 331 | } 332 | 333 | - (void)mouseMoved:(NSEvent*) event { 334 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 335 | NSLog(@"Mouse pos: %lf, %lf", point.x, point.y); 336 | } 337 | 338 | - (void) mouseDragged: (NSEvent*) event { 339 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 340 | NSLog(@"Mouse pos: %lf, %lf", point.x, point.y); 341 | } 342 | 343 | - (void)scrollWheel: (NSEvent*) event { 344 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 345 | NSLog(@"Mouse wheel at: %lf, %lf. Delta: %lf", point.x, point.y, [event deltaY] * 10.0); 346 | } 347 | 348 | - (void) mouseDown: (NSEvent*) event { 349 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 350 | NSLog(@"Left mouse down: %lf, %lf", point.x, point.y); 351 | } 352 | 353 | - (void) mouseUp: (NSEvent*) event { 354 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 355 | NSLog(@"Left mouse up: %lf, %lf", point.x, point.y); 356 | } 357 | 358 | - (void) rightMouseDown: (NSEvent*) event { 359 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 360 | NSLog(@"Right mouse down: %lf, %lf", point.x, point.y); 361 | } 362 | 363 | - (void) rightMouseUp: (NSEvent*) event { 364 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 365 | NSLog(@"Right mouse up: %lf, %lf", point.x, point.y); 366 | } 367 | 368 | - (void)otherMouseDown: (NSEvent*) event { 369 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 370 | NSLog(@"Middle mouse down: %lf, %lf", point.x, point.y); 371 | } 372 | 373 | - (void)otherMouseUp: (NSEvent*) event { 374 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 375 | NSLog(@"Middle mouse up: %lf, %lf", point.x, point.y); 376 | } 377 | 378 | - (void) mouseEntered: (NSEvent*)event { 379 | NSLog(@"Mouse entered"); 380 | } 381 | 382 | - (void) mouseExited: (NSEvent*)event { 383 | NSLog(@"Mouse left"); 384 | } 385 | 386 | - (void) keyDown: (NSEvent*) event { 387 | if ([event isARepeat] == NO) { 388 | NSLog(@"Key down: %d", [event keyCode]); 389 | } 390 | } 391 | 392 | - (void) keyUp: (NSEvent*) event { 393 | NSLog(@"Key up: %d", [event keyCode]); 394 | } 395 | 396 | // Resize 397 | - (void)windowDidResize:(NSNotification*)notification { 398 | NSSize size = [ [ _window contentView ] frame ].size; 399 | NSLog(@"Window resize: %lf, %lf", size.width, size.height); 400 | } 401 | 402 | // Terminate window when the red X is pressed 403 | -(void)windowWillClose:(NSNotification *)notification { 404 | if (running) { 405 | running = false; 406 | 407 | NSLog(@"Cleanup"); 408 | } 409 | 410 | [NSApp terminate:self]; 411 | } 412 | 413 | // Cleanup 414 | - (void) dealloc { 415 | [super dealloc]; 416 | } 417 | @end 418 | 419 | int main(int argc, const char * argv[]) { 420 | // Autorelease Pool: 421 | // Objects declared in this scope will be automatically 422 | // released at the end of it, when the pool is "drained". 423 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 424 | 425 | // Create a shared app instance. 426 | // This will initialize the global variable 427 | // 'NSApp' with the application instance. 428 | [NSApplication sharedApplication]; 429 | 430 | // Create a window: 431 | 432 | // Style flags 433 | NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask; 434 | 435 | // Window bounds (x, y, width, height) 436 | NSRect screenRect = [[NSScreen mainScreen] frame]; 437 | NSRect viewRect = NSMakeRect(0, 0, 800, 600); 438 | NSRect windowRect = NSMakeRect(NSMidX(screenRect) - NSMidX(viewRect), 439 | NSMidY(screenRect) - NSMidY(viewRect), 440 | viewRect.size.width, 441 | viewRect.size.height); 442 | 443 | NSWindow * window = [[NSWindow alloc] initWithContentRect:windowRect 444 | styleMask:windowStyle 445 | backing:NSBackingStoreBuffered 446 | defer:NO]; 447 | [window autorelease]; 448 | 449 | // Window controller 450 | NSWindowController * windowController = [[NSWindowController alloc] initWithWindow:window]; 451 | [windowController autorelease]; 452 | 453 | // Since Snow Leopard, programs without application bundles and Info.plist files don't get a menubar 454 | // and can't be brought to the front unless the presentation option is changed 455 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 456 | 457 | // Next, we need to create the menu bar. You don't need to give the first item in the menubar a name 458 | // (it will get the application's name automatically) 459 | id menubar = [[NSMenu new] autorelease]; 460 | id appMenuItem = [[NSMenuItem new] autorelease]; 461 | [menubar addItem:appMenuItem]; 462 | [NSApp setMainMenu:menubar]; 463 | 464 | // Then we add the quit item to the menu. Fortunately the action is simple since terminate: is 465 | // already implemented in NSApplication and the NSApplication is always in the responder chain. 466 | id appMenu = [[NSMenu new] autorelease]; 467 | id appName = [[NSProcessInfo processInfo] processName]; 468 | id quitTitle = [@"Quit " stringByAppendingString:appName]; 469 | id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle 470 | action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; 471 | [appMenu addItem:quitMenuItem]; 472 | [appMenuItem setSubmenu:appMenu]; 473 | 474 | // Create app delegate to handle system events 475 | View* view = [[[View alloc] initWithFrame:windowRect] autorelease]; 476 | [window setAcceptsMouseMovedEvents:YES]; 477 | [window setContentView:view]; 478 | [window setDelegate:view]; 479 | 480 | // Set app title 481 | [window setTitle:appName]; 482 | 483 | // Add fullscreen button 484 | [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; 485 | 486 | // Show window and run event loop 487 | [window orderFrontRegardless]; 488 | [NSApp run]; 489 | 490 | [pool drain]; 491 | 492 | return (0); 493 | } 494 | ``` 495 | #Part 3, Update 496 | We're going to use the quartz display link to update. First, the __CVDisplayLink__ header needs to be included 497 | ``` 498 | #import 499 | ``` 500 | Next, forward declare the function that will re-schedule update 501 | ``` 502 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void*); 503 | ``` 504 | Inside our view class, include a __CVDisplayLinkRef__ variable. 505 | ``` 506 | @interface View : NSView { 507 | @public 508 | CVDisplayLinkRef displayLink; 509 | bool running; 510 | } 511 | @end 512 | ``` 513 | Set the link up in init display 514 | ``` 515 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); 516 | CVDisplayLinkSetOutputCallback(displayLink, &GlobalDisplayLinkCallback, self); 517 | CVDisplayLinkStart(displayLink); 518 | ``` 519 | Then we implement the __getFrameForTime__ function 520 | ``` 521 | - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime { 522 | NSLog(@"Update"); 523 | if (false) { // Update loop returns false 524 | [NSApp terminate:self]; 525 | } 526 | return kCVReturnSuccess; 527 | } 528 | ``` 529 | Inside the *windowWillClose* function, the display link needs to be killed 530 | ``` 531 | CVDisplayLinkStop(displayLink); 532 | CVDisplayLinkRelease(displayLink); 533 | ``` 534 | Finally the actual _GlobalDisplayLinkCallback_ function that was declared earlyer 535 | ``` 536 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { 537 | CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime]; 538 | return result; 539 | } 540 | ``` 541 | ### Sample Code 542 | ``` 543 | // gcc Cocoa.m -o OSXWindow -framework Cocoa -framework Quartz 544 | 545 | #import 546 | #import 547 | 548 | @class View; 549 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void*); 550 | 551 | @interface View : NSView { 552 | @public 553 | CVDisplayLinkRef displayLink; 554 | bool running; 555 | } 556 | @end 557 | 558 | @implementation View 559 | // Initialize 560 | - (id) initWithFrame: (NSRect) frame { 561 | self = [super initWithFrame:frame]; 562 | running = true; 563 | // TODO: Init OpenGL 564 | 565 | NSLog(@"initialize"); 566 | 567 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); 568 | CVDisplayLinkSetOutputCallback(displayLink, &GlobalDisplayLinkCallback, self); 569 | CVDisplayLinkStart(displayLink); 570 | 571 | return self; 572 | } 573 | 574 | // Update 575 | - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime { 576 | NSLog(@"Update"); 577 | if (false) { // Update loop returns false 578 | [NSApp terminate:self]; 579 | } 580 | return kCVReturnSuccess; 581 | } 582 | 583 | // Terminate window when the red X is pressed 584 | -(void)windowWillClose:(NSNotification *)notification { 585 | if (running) { 586 | running = false; 587 | CVDisplayLinkStop(displayLink); 588 | CVDisplayLinkRelease(displayLink); 589 | 590 | NSLog(@"Cleanup"); 591 | } 592 | 593 | [NSApp terminate:self]; 594 | } 595 | 596 | // Cleanup 597 | - (void) dealloc { 598 | [super dealloc]; 599 | } 600 | @end 601 | 602 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { 603 | CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime]; 604 | return result; 605 | } 606 | 607 | int main(int argc, const char * argv[]) { 608 | // Autorelease Pool: 609 | // Objects declared in this scope will be automatically 610 | // released at the end of it, when the pool is "drained". 611 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 612 | 613 | // Create a shared app instance. 614 | // This will initialize the global variable 615 | // 'NSApp' with the application instance. 616 | [NSApplication sharedApplication]; 617 | 618 | // Create a window: 619 | 620 | // Style flags 621 | NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask; 622 | 623 | // Window bounds (x, y, width, height) 624 | NSRect screenRect = [[NSScreen mainScreen] frame]; 625 | NSRect viewRect = NSMakeRect(0, 0, 800, 600); 626 | NSRect windowRect = NSMakeRect(NSMidX(screenRect) - NSMidX(viewRect), 627 | NSMidY(screenRect) - NSMidY(viewRect), 628 | viewRect.size.width, 629 | viewRect.size.height); 630 | 631 | NSWindow * window = [[NSWindow alloc] initWithContentRect:windowRect 632 | styleMask:windowStyle 633 | backing:NSBackingStoreBuffered 634 | defer:NO]; 635 | [window autorelease]; 636 | 637 | // Window controller 638 | NSWindowController * windowController = [[NSWindowController alloc] initWithWindow:window]; 639 | [windowController autorelease]; 640 | 641 | // Since Snow Leopard, programs without application bundles and Info.plist files don't get a menubar 642 | // and can't be brought to the front unless the presentation option is changed 643 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 644 | 645 | // Next, we need to create the menu bar. You don't need to give the first item in the menubar a name 646 | // (it will get the application's name automatically) 647 | id menubar = [[NSMenu new] autorelease]; 648 | id appMenuItem = [[NSMenuItem new] autorelease]; 649 | [menubar addItem:appMenuItem]; 650 | [NSApp setMainMenu:menubar]; 651 | 652 | // Then we add the quit item to the menu. Fortunately the action is simple since terminate: is 653 | // already implemented in NSApplication and the NSApplication is always in the responder chain. 654 | id appMenu = [[NSMenu new] autorelease]; 655 | id appName = [[NSProcessInfo processInfo] processName]; 656 | id quitTitle = [@"Quit " stringByAppendingString:appName]; 657 | id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle 658 | action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; 659 | [appMenu addItem:quitMenuItem]; 660 | [appMenuItem setSubmenu:appMenu]; 661 | 662 | // Create app delegate to handle system events 663 | View* view = [[[View alloc] initWithFrame:windowRect] autorelease]; 664 | [window setAcceptsMouseMovedEvents:YES]; 665 | [window setContentView:view]; 666 | [window setDelegate:view]; 667 | 668 | // Set app title 669 | [window setTitle:appName]; 670 | 671 | // Add fullscreen button 672 | [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; 673 | 674 | // Show window and run event loop 675 | [window orderFrontRegardless]; 676 | [NSApp run]; 677 | 678 | [pool drain]; 679 | 680 | return (0); 681 | } 682 | ``` 683 | #Part 4, Getting an OpenGL Context 684 | First lets include two additional headers for OpenGL 685 | ``` 686 | #import 687 | #include 688 | ``` 689 | Next, change the __View__ class to extend __NSOpenGLView__ instead of __NSView__. We will also add two variables to the class, one that is the current Window Rect, and a lock so we can update and draw safely. 690 | ``` 691 | @interface View : NSOpenGLView { 692 | @public 693 | CVDisplayLinkRef displayLink; 694 | bool running; 695 | NSRect windowRect; 696 | NSRecursiveLock* appLock; 697 | } 698 | @end 699 | ``` 700 | Insize *initWithFrame* we will try to make a new pixel format. This format is needed to create the new OpenGL window as _super:initWithFrame_ will now take a pixel format. We also remove the code to create the *CVDisplayLink* 701 | ``` 702 | // No multisampling 703 | int samples = 0; 704 | 705 | // Keep multisampling attributes at the start of the attribute lists since code below assumes they are array elements 0 through 4. 706 | NSOpenGLPixelFormatAttribute windowedAttrs[] = 707 | { 708 | NSOpenGLPFAMultisample, 709 | NSOpenGLPFASampleBuffers, samples ? 1 : 0, 710 | NSOpenGLPFASamples, samples, 711 | NSOpenGLPFAAccelerated, 712 | NSOpenGLPFADoubleBuffer, 713 | NSOpenGLPFAColorSize, 32, 714 | NSOpenGLPFADepthSize, 24, 715 | NSOpenGLPFAAlphaSize, 8, 716 | NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy, 717 | 0 718 | }; 719 | 720 | // Try to choose a supported pixel format 721 | NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowedAttrs]; 722 | 723 | if (!pf) { 724 | NSLog(@"OpenGL pixel format not supported."); 725 | return nil; 726 | } 727 | 728 | self = [super initWithFrame:frame pixelFormat:[pf autorelease]]; 729 | ``` 730 | Finally, we initialize the lock 731 | ``` 732 | appLock = [[NSRecursiveLock alloc] init]; 733 | ``` 734 | Next we must implement the __prepareOpenGL__ method inherited from *NSOpenGLView*. First, call the same method of the base class, then we will set the OpenGL context of the view as the current context and turn on VSynch. We also move the code to create a display link into this section of the application, the display link will be synched to our OpenGL refresh rate. Finally, we start the display link. 735 | ``` 736 | - (void) prepareOpenGL { 737 | [super prepareOpenGL]; 738 | 739 | // Make all the OpenGL calls to setup rendering and build the necessary rendering objects 740 | [[self openGLContext] makeCurrentContext]; 741 | // Synchronize buffer swaps with vertical refresh rate 742 | GLint swapInt = 1; // Vsynch on! 743 | [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 744 | 745 | // Create a display link capable of being used with all active displays 746 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); 747 | 748 | // Set the renderer output callback function 749 | CVDisplayLinkSetOutputCallback(displayLink, &GlobalDisplayLinkCallback, self); 750 | 751 | CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj]; 752 | CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj]; 753 | CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); 754 | 755 | GLint dim[2] = {windowRect.size.width, windowRect.size.height}; 756 | CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim); 757 | CGLEnable(cglContext, kCGLCESurfaceBackingSize); 758 | 759 | [appLock lock]; 760 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 761 | NSLog(@"Initialize"); 762 | 763 | NSLog(@"GL version: %s", glGetString(GL_VERSION)); 764 | NSLog(@"GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); 765 | 766 | glClearColor(0.5f, 0.6f, 0.7f, 1.0f); 767 | glViewport(0, 0, windowRect.size.width, windowRect.size.height); 768 | glEnable(GL_DEPTH_TEST); 769 | 770 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 771 | [appLock unlock]; 772 | 773 | // Activate the display link 774 | CVDisplayLinkStart(displayLink); 775 | } 776 | ``` 777 | Every method we implemented will need to lock and unlock appLock like the above code sample does. The *getFrameForTime* method will simply clear the frame color for now. Once the update is finished we present the context 778 | ``` 779 | // Update 780 | - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime { 781 | [appLock lock]; 782 | 783 | [[self openGLContext] makeCurrentContext]; 784 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 785 | 786 | NSLog(@"Update"); 787 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 788 | NSLog(@"Render"); 789 | 790 | CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]); 791 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 792 | 793 | if (false) { // Update loop returns false 794 | [NSApp terminate:self]; 795 | } 796 | 797 | [appLock unlock]; 798 | 799 | return kCVReturnSuccess; 800 | } 801 | ``` 802 | The *windowDidResize* function will now keep track of the window size, and set the glViewPort 803 | ``` 804 | - (void)windowDidResize:(NSNotification*)notification { 805 | NSSize size = [ [ _window contentView ] frame ].size; 806 | [appLock lock]; 807 | [[self openGLContext] makeCurrentContext]; 808 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 809 | NSLog(@"Window resize: %lf, %lf", size.width, size.height); 810 | 811 | windowRect.size.width = size.width; 812 | windowRect.size.height = size.height; 813 | glViewport(0, 0, windowRect.size.width, windowRect.size.height); 814 | 815 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 816 | [appLock unlock]; 817 | } 818 | ``` 819 | We introduce two more inherited methods, __resumeDisplayRenderer__ and __haltDisplayRenderer__. Both of these methods are simply going to lock *appLock*, stop the *CVDispayLink* and unlock. 820 | ``` 821 | [appLock lock]; 822 | CVDisplayLinkStop(displayLink); 823 | [appLock unlock]; 824 | ``` 825 | The __windowWillClose__ method must lock and unlock before stopping the display link as well 826 | ``` 827 | if (running) { 828 | running = false; 829 | 830 | [appLock lock]; 831 | NSLog(@"Cleanup"); 832 | 833 | CVDisplayLinkStop(displayLink); 834 | CVDisplayLinkRelease(displayLink); 835 | 836 | [appLock unlock]; 837 | } 838 | ``` 839 | And finally, we release the appLock inside of the dealloc function 840 | ``` 841 | - (void) dealloc { 842 | [appLock release]; 843 | [super dealloc]; 844 | } 845 | ``` 846 | ###Sample Code 847 | ``` 848 | // gcc Make.m -o MWindow -framework Cocoa -framework Quartz -framework OpenGL 849 | 850 | #import 851 | #import 852 | #import 853 | #include 854 | 855 | @class View; 856 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void*); 857 | 858 | @interface View : NSOpenGLView { 859 | @public 860 | CVDisplayLinkRef displayLink; 861 | bool running; 862 | NSRect windowRect; 863 | NSRecursiveLock* appLock; 864 | } 865 | @end 866 | 867 | @implementation View 868 | // Initialize 869 | - (id) initWithFrame: (NSRect) frame { 870 | running = true; 871 | 872 | // No multisampling 873 | int samples = 0; 874 | 875 | // Keep multisampling attributes at the start of the attribute lists since code below assumes they are array elements 0 through 4. 876 | NSOpenGLPixelFormatAttribute windowedAttrs[] = 877 | { 878 | NSOpenGLPFAMultisample, 879 | NSOpenGLPFASampleBuffers, samples ? 1 : 0, 880 | NSOpenGLPFASamples, samples, 881 | NSOpenGLPFAAccelerated, 882 | NSOpenGLPFADoubleBuffer, 883 | NSOpenGLPFAColorSize, 32, 884 | NSOpenGLPFADepthSize, 24, 885 | NSOpenGLPFAAlphaSize, 8, 886 | NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy, 887 | 0 888 | }; 889 | 890 | // Try to choose a supported pixel format 891 | NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowedAttrs]; 892 | 893 | if (!pf) { 894 | NSLog(@"OpenGL pixel format not supported."); 895 | return nil; 896 | } 897 | 898 | self = [super initWithFrame:frame pixelFormat:[pf autorelease]]; 899 | appLock = [[NSRecursiveLock alloc] init]; 900 | 901 | return self; 902 | } 903 | 904 | - (void) prepareOpenGL { 905 | [super prepareOpenGL]; 906 | 907 | // Make all the OpenGL calls to setup rendering and build the necessary rendering objects 908 | [[self openGLContext] makeCurrentContext]; 909 | // Synchronize buffer swaps with vertical refresh rate 910 | GLint swapInt = 1; // Vsynch on! 911 | [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 912 | 913 | // Create a display link capable of being used with all active displays 914 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); 915 | 916 | // Set the renderer output callback function 917 | CVDisplayLinkSetOutputCallback(displayLink, &GlobalDisplayLinkCallback, self); 918 | 919 | CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj]; 920 | CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj]; 921 | CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); 922 | 923 | GLint dim[2] = {windowRect.size.width, windowRect.size.height}; 924 | CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim); 925 | CGLEnable(cglContext, kCGLCESurfaceBackingSize); 926 | 927 | [appLock lock]; 928 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 929 | NSLog(@"Initialize"); 930 | 931 | NSLog(@"GL version: %s", glGetString(GL_VERSION)); 932 | NSLog(@"GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); 933 | 934 | glClearColor(0.5f, 0.6f, 0.7f, 1.0f); 935 | glViewport(0, 0, windowRect.size.width, windowRect.size.height); 936 | glEnable(GL_DEPTH_TEST); 937 | 938 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 939 | [appLock unlock]; 940 | 941 | // Activate the display link 942 | CVDisplayLinkStart(displayLink); 943 | } 944 | 945 | // Tell the window to accept input events 946 | - (BOOL)acceptsFirstResponder { 947 | return YES; 948 | } 949 | 950 | // Update 951 | - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime { 952 | [appLock lock]; 953 | 954 | [[self openGLContext] makeCurrentContext]; 955 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 956 | 957 | NSLog(@"Update"); 958 | // Temp 959 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 960 | // EndTemp 961 | NSLog(@"Render"); 962 | 963 | CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]); 964 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 965 | 966 | if (false) { // Update loop returns false 967 | [NSApp terminate:self]; 968 | } 969 | 970 | [appLock unlock]; 971 | 972 | return kCVReturnSuccess; 973 | } 974 | 975 | // Resize 976 | - (void)windowDidResize:(NSNotification*)notification { 977 | NSSize size = [ [ _window contentView ] frame ].size; 978 | [appLock lock]; 979 | [[self openGLContext] makeCurrentContext]; 980 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 981 | NSLog(@"Window resize: %lf, %lf", size.width, size.height); 982 | // Temp 983 | windowRect.size.width = size.width; 984 | windowRect.size.height = size.height; 985 | glViewport(0, 0, windowRect.size.width, windowRect.size.height); 986 | // End temp 987 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 988 | [appLock unlock]; 989 | } 990 | 991 | - (void)resumeDisplayRenderer { 992 | [appLock lock]; 993 | CVDisplayLinkStop(displayLink); 994 | [appLock unlock]; 995 | } 996 | 997 | - (void)haltDisplayRenderer { 998 | [appLock lock]; 999 | CVDisplayLinkStop(displayLink); 1000 | [appLock unlock]; 1001 | } 1002 | 1003 | // Terminate window when the red X is pressed 1004 | -(void)windowWillClose:(NSNotification *)notification { 1005 | if (running) { 1006 | running = false; 1007 | 1008 | [appLock lock]; 1009 | NSLog(@"Cleanup"); 1010 | 1011 | CVDisplayLinkStop(displayLink); 1012 | CVDisplayLinkRelease(displayLink); 1013 | 1014 | [appLock unlock]; 1015 | } 1016 | 1017 | [NSApp terminate:self]; 1018 | } 1019 | 1020 | // Cleanup 1021 | - (void) dealloc { 1022 | [appLock release]; 1023 | [super dealloc]; 1024 | } 1025 | @end 1026 | 1027 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { 1028 | CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime]; 1029 | return result; 1030 | } 1031 | 1032 | int main(int argc, const char * argv[]) { 1033 | // Autorelease Pool: 1034 | // Objects declared in this scope will be automatically 1035 | // released at the end of it, when the pool is "drained". 1036 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 1037 | 1038 | // Create a shared app instance. 1039 | // This will initialize the global variable 1040 | // 'NSApp' with the application instance. 1041 | [NSApplication sharedApplication]; 1042 | 1043 | // Create a window: 1044 | 1045 | // Style flags 1046 | NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask; 1047 | 1048 | // Window bounds (x, y, width, height) 1049 | NSRect screenRect = [[NSScreen mainScreen] frame]; 1050 | NSRect viewRect = NSMakeRect(0, 0, 800, 600); 1051 | NSRect windowRect = NSMakeRect(NSMidX(screenRect) - NSMidX(viewRect), 1052 | NSMidY(screenRect) - NSMidY(viewRect), 1053 | viewRect.size.width, 1054 | viewRect.size.height); 1055 | 1056 | NSWindow * window = [[NSWindow alloc] initWithContentRect:windowRect 1057 | styleMask:windowStyle 1058 | backing:NSBackingStoreBuffered 1059 | defer:NO]; 1060 | [window autorelease]; 1061 | 1062 | // Window controller 1063 | NSWindowController * windowController = [[NSWindowController alloc] initWithWindow:window]; 1064 | [windowController autorelease]; 1065 | 1066 | // Since Snow Leopard, programs without application bundles and Info.plist files don't get a menubar 1067 | // and can't be brought to the front unless the presentation option is changed 1068 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 1069 | 1070 | // Next, we need to create the menu bar. You don't need to give the first item in the menubar a name 1071 | // (it will get the application's name automatically) 1072 | id menubar = [[NSMenu new] autorelease]; 1073 | id appMenuItem = [[NSMenuItem new] autorelease]; 1074 | [menubar addItem:appMenuItem]; 1075 | [NSApp setMainMenu:menubar]; 1076 | 1077 | // Then we add the quit item to the menu. Fortunately the action is simple since terminate: is 1078 | // already implemented in NSApplication and the NSApplication is always in the responder chain. 1079 | id appMenu = [[NSMenu new] autorelease]; 1080 | id appName = [[NSProcessInfo processInfo] processName]; 1081 | id quitTitle = [@"Quit " stringByAppendingString:appName]; 1082 | id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle 1083 | action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; 1084 | [appMenu addItem:quitMenuItem]; 1085 | [appMenuItem setSubmenu:appMenu]; 1086 | 1087 | // Create app delegate to handle system events 1088 | View* view = [[[View alloc] initWithFrame:windowRect] autorelease]; 1089 | view->windowRect = windowRect; 1090 | [window setAcceptsMouseMovedEvents:YES]; 1091 | [window setContentView:view]; 1092 | [window setDelegate:view]; 1093 | 1094 | // Set app title 1095 | [window setTitle:appName]; 1096 | 1097 | // Add fullscreen button 1098 | [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; 1099 | 1100 | // Show window and run event loop 1101 | [window orderFrontRegardless]; 1102 | [NSApp run]; 1103 | 1104 | [pool drain]; 1105 | 1106 | return (0); 1107 | } 1108 | ``` 1109 | 1110 | #Part 5, Modern OpenGL Context 1111 | The above is great, but running it on mavericks it gives us an OpenGL 2.1 context with support for GLSL 1.2. While this is good for testing mobile device compatibality; we want to get a modern OpenGL Context (3.0 +). 1112 | In order to do this, change the __gl.h__ include to __gl3.h__ 1113 | ``` 1114 | #include 1115 | ``` 1116 | And in the __NSOpenGLPixelFormatAttribute windowedAttrs__ variable change __NSOpenGLPFAOpenGLProfile__ to *NSOpenGLProfileVersion3_2Core*. 1117 | ``` 1118 | NSOpenGLPixelFormatAttribute windowedAttrs[] = { 1119 | NSOpenGLPFAMultisample, 1120 | NSOpenGLPFASampleBuffers, samples ? 1 : 0, 1121 | NSOpenGLPFASamples, samples, 1122 | NSOpenGLPFAAccelerated, 1123 | NSOpenGLPFADoubleBuffer, 1124 | NSOpenGLPFAColorSize, 32, 1125 | NSOpenGLPFADepthSize, 24, 1126 | NSOpenGLPFAAlphaSize, 8, 1127 | NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, 1128 | 0 1129 | }; 1130 | ``` 1131 | That's it. You should now have a 3.2+ context. On My MBA it gives me a 4.2 context. The below "Putting it all together" section will use the legacy context. 1132 | 1133 | #Part 6, Putting it all together 1134 | ``` 1135 | // gcc Cocoa.m -o OSXWindow -framework Cocoa -framework Quartz -framework OpenGL 1136 | 1137 | #import 1138 | #import 1139 | #import 1140 | #include 1141 | 1142 | @class View; 1143 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void*); 1144 | 1145 | @interface View : NSOpenGLView { 1146 | @public 1147 | CVDisplayLinkRef displayLink; 1148 | bool running; 1149 | NSRect windowRect; 1150 | NSRecursiveLock* appLock; 1151 | } 1152 | @end 1153 | 1154 | @implementation View 1155 | // Initialize 1156 | - (id) initWithFrame: (NSRect) frame { 1157 | running = true; 1158 | 1159 | // No multisampling 1160 | int samples = 0; 1161 | 1162 | // Keep multisampling attributes at the start of the attribute lists since code below assumes they are array elements 0 through 4. 1163 | NSOpenGLPixelFormatAttribute windowedAttrs[] = 1164 | { 1165 | NSOpenGLPFAMultisample, 1166 | NSOpenGLPFASampleBuffers, samples ? 1 : 0, 1167 | NSOpenGLPFASamples, samples, 1168 | NSOpenGLPFAAccelerated, 1169 | NSOpenGLPFADoubleBuffer, 1170 | NSOpenGLPFAColorSize, 32, 1171 | NSOpenGLPFADepthSize, 24, 1172 | NSOpenGLPFAAlphaSize, 8, 1173 | NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy, 1174 | 0 1175 | }; 1176 | 1177 | // Try to choose a supported pixel format 1178 | NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowedAttrs]; 1179 | 1180 | if (!pf) { 1181 | bool valid = false; 1182 | while (!pf && samples > 0) { 1183 | samples /= 2; 1184 | windowedAttrs[2] = samples ? 1 : 0; 1185 | windowedAttrs[4] = samples; 1186 | pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowedAttrs]; 1187 | if (pf) { 1188 | valid = true; 1189 | break; 1190 | } 1191 | } 1192 | 1193 | if (!valid) { 1194 | NSLog(@"OpenGL pixel format not supported."); 1195 | return nil; 1196 | } 1197 | } 1198 | 1199 | self = [super initWithFrame:frame pixelFormat:[pf autorelease]]; 1200 | appLock = [[NSRecursiveLock alloc] init]; 1201 | 1202 | return self; 1203 | } 1204 | 1205 | - (void) prepareOpenGL { 1206 | [super prepareOpenGL]; 1207 | 1208 | [[self window] setLevel: NSNormalWindowLevel]; 1209 | [[self window] makeKeyAndOrderFront: self]; 1210 | 1211 | // Make all the OpenGL calls to setup rendering and build the necessary rendering objects 1212 | [[self openGLContext] makeCurrentContext]; 1213 | // Synchronize buffer swaps with vertical refresh rate 1214 | GLint swapInt = 1; // Vsynch on! 1215 | [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 1216 | 1217 | // Create a display link capable of being used with all active displays 1218 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); 1219 | 1220 | // Set the renderer output callback function 1221 | CVDisplayLinkSetOutputCallback(displayLink, &GlobalDisplayLinkCallback, self); 1222 | 1223 | CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj]; 1224 | CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj]; 1225 | CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); 1226 | 1227 | GLint dim[2] = {windowRect.size.width, windowRect.size.height}; 1228 | CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim); 1229 | CGLEnable(cglContext, kCGLCESurfaceBackingSize); 1230 | 1231 | [appLock lock]; 1232 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 1233 | NSLog(@"Initialize"); 1234 | 1235 | NSLog(@"GL version: %s", glGetString(GL_VERSION)); 1236 | NSLog(@"GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); 1237 | // Temp 1238 | glClearColor(0.5f, 0.6f, 0.7f, 1.0f); 1239 | glViewport(0, 0, windowRect.size.width, windowRect.size.height); 1240 | glEnable(GL_DEPTH_TEST); 1241 | // End temp 1242 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 1243 | [appLock unlock]; 1244 | 1245 | // Activate the display link 1246 | CVDisplayLinkStart(displayLink); 1247 | } 1248 | 1249 | // Tell the window to accept input events 1250 | - (BOOL)acceptsFirstResponder { 1251 | return YES; 1252 | } 1253 | 1254 | - (void)mouseMoved:(NSEvent*) event { 1255 | [appLock lock]; 1256 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1257 | NSLog(@"Mouse pos: %lf, %lf", point.x, point.y); 1258 | [appLock unlock]; 1259 | } 1260 | 1261 | - (void) mouseDragged: (NSEvent*) event { 1262 | [appLock lock]; 1263 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1264 | NSLog(@"Mouse pos: %lf, %lf", point.x, point.y); 1265 | [appLock unlock]; 1266 | } 1267 | 1268 | - (void)scrollWheel: (NSEvent*) event { 1269 | [appLock lock]; 1270 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1271 | NSLog(@"Mouse wheel at: %lf, %lf. Delta: %lf", point.x, point.y, [event deltaY]); 1272 | [appLock unlock]; 1273 | } 1274 | 1275 | - (void) mouseDown: (NSEvent*) event { 1276 | [appLock lock]; 1277 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1278 | NSLog(@"Left mouse down: %lf, %lf", point.x, point.y); 1279 | [appLock unlock]; 1280 | } 1281 | 1282 | - (void) mouseUp: (NSEvent*) event { 1283 | [appLock lock]; 1284 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1285 | NSLog(@"Left mouse up: %lf, %lf", point.x, point.y); 1286 | [appLock unlock]; 1287 | } 1288 | 1289 | - (void) rightMouseDown: (NSEvent*) event { 1290 | [appLock lock]; 1291 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1292 | NSLog(@"Right mouse down: %lf, %lf", point.x, point.y); 1293 | [appLock unlock]; 1294 | } 1295 | 1296 | - (void) rightMouseUp: (NSEvent*) event { 1297 | [appLock lock]; 1298 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1299 | NSLog(@"Right mouse up: %lf, %lf", point.x, point.y); 1300 | [appLock unlock]; 1301 | } 1302 | 1303 | - (void)otherMouseDown: (NSEvent*) event { 1304 | [appLock lock]; 1305 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1306 | NSLog(@"Middle mouse down: %lf, %lf", point.x, point.y); 1307 | [appLock unlock]; 1308 | } 1309 | 1310 | - (void)otherMouseUp: (NSEvent*) event { 1311 | [appLock lock]; 1312 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 1313 | NSLog(@"Middle mouse up: %lf, %lf", point.x, point.y); 1314 | [appLock unlock]; 1315 | } 1316 | 1317 | - (void) mouseEntered: (NSEvent*)event { 1318 | [appLock lock]; 1319 | NSLog(@"Mouse entered"); 1320 | [appLock unlock]; 1321 | } 1322 | 1323 | - (void) mouseExited: (NSEvent*)event { 1324 | [appLock lock]; 1325 | NSLog(@"Mouse left"); 1326 | [appLock unlock]; 1327 | } 1328 | 1329 | - (void) keyDown: (NSEvent*) event { 1330 | [appLock lock]; 1331 | if ([event isARepeat] == NO) { 1332 | NSLog(@"Key down: %d", [event keyCode]); 1333 | } 1334 | [appLock unlock]; 1335 | } 1336 | 1337 | - (void) keyUp: (NSEvent*) event { 1338 | [appLock lock]; 1339 | NSLog(@"Key up: %d", [event keyCode]); 1340 | [appLock unlock]; 1341 | } 1342 | 1343 | // Update 1344 | - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime { 1345 | [appLock lock]; 1346 | 1347 | [[self openGLContext] makeCurrentContext]; 1348 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 1349 | 1350 | NSLog(@"Update"); 1351 | // Temp 1352 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 1353 | 1354 | // EndTemp 1355 | 1356 | CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]); 1357 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 1358 | 1359 | if (false) { // Update loop returns false 1360 | [NSApp terminate:self]; 1361 | } 1362 | 1363 | [appLock unlock]; 1364 | 1365 | return kCVReturnSuccess; 1366 | } 1367 | 1368 | // Resize 1369 | - (void)windowDidResize:(NSNotification*)notification { 1370 | NSSize size = [ [ _window contentView ] frame ].size; 1371 | [appLock lock]; 1372 | [[self openGLContext] makeCurrentContext]; 1373 | CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 1374 | NSLog(@"Window resize: %lf, %lf", size.width, size.height); 1375 | // Temp 1376 | windowRect.size.width = size.width; 1377 | windowRect.size.height = size.height; 1378 | glViewport(0, 0, windowRect.size.width, windowRect.size.height); 1379 | // End temp 1380 | CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]); 1381 | [appLock unlock]; 1382 | } 1383 | 1384 | - (void)resumeDisplayRenderer { 1385 | [appLock lock]; 1386 | CVDisplayLinkStop(displayLink); 1387 | [appLock unlock]; 1388 | } 1389 | 1390 | - (void)haltDisplayRenderer { 1391 | [appLock lock]; 1392 | CVDisplayLinkStop(displayLink); 1393 | [appLock unlock]; 1394 | } 1395 | 1396 | // Terminate window when the red X is pressed 1397 | -(void)windowWillClose:(NSNotification *)notification { 1398 | if (running) { 1399 | running = false; 1400 | 1401 | [appLock lock]; 1402 | NSLog(@"Cleanup"); 1403 | 1404 | CVDisplayLinkStop(displayLink); 1405 | CVDisplayLinkRelease(displayLink); 1406 | 1407 | [appLock unlock]; 1408 | } 1409 | 1410 | [NSApp terminate:self]; 1411 | } 1412 | 1413 | // Cleanup 1414 | - (void) dealloc { 1415 | [appLock release]; 1416 | [super dealloc]; 1417 | } 1418 | @end 1419 | 1420 | static CVReturn GlobalDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { 1421 | CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime]; 1422 | return result; 1423 | } 1424 | 1425 | int main(int argc, const char * argv[]) { 1426 | // Autorelease Pool: 1427 | // Objects declared in this scope will be automatically 1428 | // released at the end of it, when the pool is "drained". 1429 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 1430 | 1431 | // Create a shared app instance. 1432 | // This will initialize the global variable 1433 | // 'NSApp' with the application instance. 1434 | [NSApplication sharedApplication]; 1435 | 1436 | // Create a window: 1437 | 1438 | // Style flags 1439 | NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask; 1440 | 1441 | // Window bounds (x, y, width, height) 1442 | NSRect screenRect = [[NSScreen mainScreen] frame]; 1443 | NSRect viewRect = NSMakeRect(0, 0, 800, 600); 1444 | NSRect windowRect = NSMakeRect(NSMidX(screenRect) - NSMidX(viewRect), 1445 | NSMidY(screenRect) - NSMidY(viewRect), 1446 | viewRect.size.width, 1447 | viewRect.size.height); 1448 | 1449 | NSWindow * window = [[NSWindow alloc] initWithContentRect:windowRect 1450 | styleMask:windowStyle 1451 | backing:NSBackingStoreBuffered 1452 | defer:NO]; 1453 | [window autorelease]; 1454 | 1455 | // Window controller 1456 | NSWindowController * windowController = [[NSWindowController alloc] initWithWindow:window]; 1457 | [windowController autorelease]; 1458 | 1459 | // Since Snow Leopard, programs without application bundles and Info.plist files don't get a menubar 1460 | // and can't be brought to the front unless the presentation option is changed 1461 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 1462 | 1463 | // Next, we need to create the menu bar. You don't need to give the first item in the menubar a name 1464 | // (it will get the application's name automatically) 1465 | id menubar = [[NSMenu new] autorelease]; 1466 | id appMenuItem = [[NSMenuItem new] autorelease]; 1467 | [menubar addItem:appMenuItem]; 1468 | [NSApp setMainMenu:menubar]; 1469 | 1470 | // Then we add the quit item to the menu. Fortunately the action is simple since terminate: is 1471 | // already implemented in NSApplication and the NSApplication is always in the responder chain. 1472 | id appMenu = [[NSMenu new] autorelease]; 1473 | id appName = [[NSProcessInfo processInfo] processName]; 1474 | id quitTitle = [@"Quit " stringByAppendingString:appName]; 1475 | id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle 1476 | action:@selector(terminate:) keyEquivalent:@"q"] autorelease]; 1477 | [appMenu addItem:quitMenuItem]; 1478 | [appMenuItem setSubmenu:appMenu]; 1479 | 1480 | // Create app delegate to handle system events 1481 | View* view = [[[View alloc] initWithFrame:windowRect] autorelease]; 1482 | view->windowRect = windowRect; 1483 | [window setAcceptsMouseMovedEvents:YES]; 1484 | [window setContentView:view]; 1485 | [window setDelegate:view]; 1486 | 1487 | // Set app title 1488 | [window setTitle:appName]; 1489 | 1490 | // Add fullscreen button 1491 | [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; 1492 | 1493 | // Show window and run event loop 1494 | [window orderFrontRegardless]; 1495 | [NSApp run]; 1496 | 1497 | [pool drain]; 1498 | 1499 | return (0); 1500 | } 1501 | ``` 1502 | 1503 | #Sources 1504 | * https://github.com/gameplay3d/GamePlay/blob/master/gameplay%2Fsrc%2FPlatformMacOSX.mm 1505 | * http://www.mightwerk.com/blog/DC261D88-555B-494B-B563-DAC1271776FB/index.html 1506 | * https://developer.apple.com/library/mac/qa/qa1385/_index.html 1507 | * https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html 1508 | * http://fredandrandall.com/blog/2011/09/08/how-to-make-your-app-open-in-full-screen-on-lion/ 1509 | * https://casperbhansen.wordpress.com/2010/08/15/dev-tip-nibless-development/ 1510 | * http://stackoverflow.com/questions/8233141/completely-close-an-os-x-application-with-window-close-button 1511 | * http://stackoverflow.com/questions/626898/how-do-i-create-delegates-in-objective-c 1512 | * http://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_Beginners/Wikidraw's_view_class -------------------------------------------------------------------------------- /Sources.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gamedevtech/CocoaOpenGLWindow/79e327b790dfb92f50bb380ae93427608827051b/Sources.zip --------------------------------------------------------------------------------