< Previous PageNext Page > Hide TOC

Drawing to the Full Screen

In Mac OS X, you don't have to restrict your OpenGL drawing to views and windows. You also have the option to draw to the entire screen. The primary difference between drawing to a view or window and drawing to the full screen is that you must prevent other applications and system services from trying to do the same thing. You can capture the display by using the Quartz Display Services API. Once captured by your application, other applications are not notified of display changes, thus preventing them from repositioning their windows and preventing the Finder from repositioning desktop icons. The screen is all yours for OpenGL drawing.


Figure 3-1  Drawing OpenGL content to the full screen

Drawing OpenGL content to the full screen

Each of the Apple-specific OpenGL APIs provides routines for setting up full-screen drawing. The approach for using each is similar, as you'll see by reading the first section in this chapter, which describes the general approach. This chapter also provides specific information for using each of the Apple-specific OpenGL APIs and shows how to use Quartz Display Services to switch the display mode and change screen resolutions, two tasks that are useful for any application that uses the full screen.

In this section:

General Approach
Using Cocoa to Create a Full-Screen Context
Using AGL to Create a Full-Screen Context
Using CGL to Create a Full-Screen Context
Adjusting Display Modes
What's Next?
See Also


General Approach

Many of the tasks for setting up full-screen drawing are similar to those required to set up drawing OpenGL content to a Cocoa view or a Carbon window. The tasks that are similar are explained in detail in “Drawing to a Window or View” but only mentioned here. If you haven't read that chapter, you should read it first.

Drawing OpenGL content to a full screen requires performing the following tasks:

  1. Capture the display you want to draw to by calling the Quartz Display Services function CGDisplayCapture and supplying a display ID that represents a unique ID for an attached display. The constant kCGDirectMainDisplay represents the main display, the one that’s shown in the menu bar.

    If you want to capture all the displays attached to a system, call the function CGDisplayCaptureAllDisplays.

  2. Convert the display ID to an OpenGL display mask by calling the function CGDisplayIDToOpenGLDisplayMask.

  3. Set up the renderer and buffer attributes that support the OpenGL drawing you want to perform, making sure to include a full-screen attribute and the OpenGL display mask that you obtained in the previous step.

  4. Request a pixel format object that encapsulates the renderer and buffer attributes required by your application.

    Some OpenGL renderers, such as the software renderer, do not support full-screen mode. If the system returns NULL for the pixel format object, your application needs to take appropriate action.

  5. Create a rendering context and bind the pixel format object to it.

  6. Release the pixel format object.

  7. Make the context the current context.

  8. Bind a full-screen drawable object to the rendering context.

  9. Perform your drawing.

  10. When you are done drawing, perform the necessary cleanup work and make sure that you release the captured display.

Using Cocoa to Create a Full-Screen Context

When you set up an attributes array, you need to include the attribute NSOpenGLPFAFullScreen to specify that only renderers that are capable of rendering to the full screen should be considered when the system creates a pixel format object. You also need to include the attribute NSOpenGLPFAScreenMask along with the appropriate OpenGL display mask.

Listing 3-1 is a code fragment that shows how to use the NSOpenGLPixelFormat and NSOpenGLContext classes along with calls from Quartz Display Services to set up full-screen drawing in a Cocoa application. A detailed explanation for each numbered line of code appears following the listing.

Listing 3-1  Using Cocoa to set up full-screen drawing

CGDisplayErr err;
NSOpenGLContext *fullScreenContext;
NSOpenGLPixelFormatAttribute attrs[] = { // 1
    NSOpenGLPFAFullScreen,
    NSOpenGLPFAScreenMask,
                CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay),
    NSOpenGLPFAColorSize, 24,  // 2
    NSOpenGLPFADepthSize, 16,
    NSOpenGLPFADoubleBuffer,
    NSOpenGLPFAAccelerated,
    0
};
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc]
                                initWithAttributes:attrs];
fullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
                         shareContext:NULL];
[pixelFormat release];
pixelFormat = nil;
if (fullScreenContext == nil) {
        NSLog(@"Failed to create fullScreenContext");
        return;
}
err = CGCaptureAllDisplays(); // 3
if (err != CGDisplayNoErr) {
        [fullScreenContext release];
        fullScreenContext = nil;
        return;
}
[fullScreenContext setFullScreen]; // 4
[fullScreenContext makeCurrentContext]; // 5

Here's what the code does:

  1. Sets up an array of renderer and buffer attributes, including the appropriate attributes to specify full-screen mode and the display ID for the main display. This example also supplies a number of other attributes. You would supply the attributes that are appropriate for your application.

  2. Supplies a color size that matches the current display depth. Note that this value must match the current display depth.

  3. Calls the Quartz Display Services function that captures all displays. If you want to capture only one display, you can call the function CGDisplayCapture, passing the ID of the display that you want to capture.

  4. Attaches the full-screen drawable object to the rendering context.

  5. Makes the full-screen context the current context that will receive OpenGL commands. If you fail to perform this step, you won't see any content drawn to the screen.

When you no longer need to draw full-screen OpenGL content, you must release resources and release the captured display (or displays).

Using AGL to Create a Full-Screen Context

This extended code example is an excerpt from an application that uses an application-defined structure—pRecContext—to store information about the context, including display IDs for the displays attached to the system and a rendering context. The MySetupAGL routine in Listing 3-2 takes as parameters a pRecContext data type, a width and height that specifies the screen resolution, a bit depth, and the refresh rate of the display.

The MySetupAGL routine sets the display mode and sets up a full-screen context. A detailed explanation for each numbered line of code appears following the listing.

Listing 3-2  A function that sets up a full-screen context using AGL

OSStatus MySetupAGL (pRecContext pContextInfo, size_t width, size_t height,
                        size_t depth, CGRefreshRate refresh)
{
    OSStatus err = noErr;
    GLint attribs[] = { AGL_RGBA, AGL_NO_RECOVERY,
            AGL_FULLSCREEN, AGL_DOUBLEBUFFER,
            AGL_DEPTH_SIZE, 32,
                        0 }; // 1
    AGLPixelFormat pixelFormat = NULL;
    long i, index;
    GDHandle gdhDisplay;
    CFDictionaryRef refDisplayMode = 0;
 
    if (NULL == pContextInfo)
        return paramErr;
    refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
                            pContextInfo->display,
                            depth, width, height, refresh, NULL); // 2
    if (refDisplayMode) {
        gOldDisplayMode = CGDisplayCurrentMode( pContextInfo->display); // 3
        gOldDisplayModeValid = GL_TRUE;
        CGDisplaySwitchToMode (pContextInfo->display, refDisplayMode); // 4
    }
    for (i = 0; i < gNumDisplays; i++) { // 5
        if (pContextInfo->display == gDisplayCaps[i].cgDisplayID) index = i;
    }
    err = DMGetGDeviceByDisplayID ((DisplayIDType)pContextInfo->display,
                                 &gdhDisplay, false); // 6
    if (noErr == err)
        if (!(pixelFormat = aglChoosePixelFormat (&gdhDisplay, 1, attribs)))
            err = aglReportError ();
    if (pixelFormat) {
        if (!(pContextInfo->aglContext = aglCreateContext( pixelFormat,
                                                         NULL)))
            err = aglReportError ();
        aglDestroyPixelFormat (pixelFormat);
    }
    if (pContextInfo->aglContext) {
        short fNum;
        GLint swap = 1;
        if (!aglSetCurrentContext (pContextInfo->aglContext)) // 7
            err = aglReportError ();
        if ((noErr == err) && !aglSetFullScreen( pContextInfo->aglContext,
                                            0, 0, 0, 0)) // 8
            err = aglReportError ();
        if (noErr == err) {
            if (!aglSetInteger (pContextInfo->aglContext,
                                AGL_SWAP_INTERVAL, &swap)); // 9
                err = aglReportError ();
            /* Your code to perform other initializations here */
        }
    }
    return err;
}

Here's what the code does:

  1. Sets up renderer and buffer attributes. You must supply AGL_FULLSCREEN when you want to set up a full-screen context using the AGL API. This example also provides a number of other attributes: RGBA pixel format, double buffering, a depth size of 32 bits, and the no recovery attribute. No recovery indicates that if a suitable hardware renderer isn't found, the operating system should not substitute a software renderer.

  2. Obtains the best display mode for the screen resolution, bit depth, and refresh rate passed to the MySetupAGL function.

  3. Gets the current display mode and then saves it so that it can be restored later. It's recommended practice for you to save and restore the display mode.

  4. Switches to the display mode.

  5. Gets the display capabilities of interest for current display. For more information on determining the capabilities of a display, see “Determining the OpenGL Capabilities Supported by the Hardware.”

  6. Calls the Display Manager function that obtains a handle for the video device with the specified display ID. You must pass this handle to aglChoosePixelFormat.

  7. Sets the current context to the newly created context. If you fail to perform this task, you won't see any OpenGL content drawn on the screen.

  8. Attaches the full-screen drawable object to the rendering context.

  9. Synchronizes to the refresh rate by setting the swap interval to 1. (Recall that the swap variable was previously assigned a value of 1). For more information, see “Synchronize with the Screen Refresh Rate.” The function aglSetInteger allows you to set a variety of rendering context parameters. For more information see “Techniques for Working with Rendering Contexts.”

Using CGL to Create a Full-Screen Context

Because the CGL API is at a lower level in the system architecture than either Cocoa or the AGL API, you can use it to create a full-screen context in either a Cocoa or a Carbon application. The code in Listing 3-3 shows how to capture the main display and create a full-screen context. As you can see, the code parallels the examples shown in “Using Cocoa to Create a Full-Screen Context” and “Using AGL to Create a Full-Screen Context.” A detailed explanation for each numbered line of code appears following the listing.

Depending on what you want to accomplish, there are a number of modifications that you can make to the code, such as adjusting the display mode and synchronizing rendering to the screen refresh rate. See “Adjusting Display Modes” and “Quartz Display Services and Full-Screen Mode.”

Listing 3-3  Setting up a full-screen context using CGL

CGDisplayCapture (kCGDirectMainDisplay); // 1
CGLPixelFormatAttribute attribs[] = { kCGLPFADoubleBuffer,
        kCGLPFAFullScreen,
        kCGLPFADisplayMask,
        CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay),
        NULL
    }; // 2
CGLPixelFormatObj pixelFormatObj;
long numPixelFormats ;
 
CGLChoosePixelFormat( attribs, &pixelFormatObj, &numPixelFormats );
 
CGLContextObj contextObj ;
CGLCreateContext( pixelFormatObj, NULL, &contextObj );
 
CGLDestroyPixelFormat( pixelFormatObj );
 
CGLSetCurrentContext( contextObj ); // 3
CGLSetFullScreen( contextObj );// 4
 
//****** Perform your application's main loop
 
CGLSetCurrentContext(NULL); // 5
CGLClearDrawable(contextObj);
CGLDestroyContext(contextObj);
CGReleaseAllDisplays();

Here's what the code does:

  1. Captures the main display.

  2. Sets up an array of attributes that includes the full-screen attribute and the display mask associated with the captured display.

  3. Sets the current context to the one it will use for full-screen drawing.

  4. Attaches a full-screen drawable object to the current context.

  5. After all drawing is completed, sets the current context to NULL, and goes on to perform the other necessary clean up work: clearing the drawable object, destroying the rendering context, and releasing the displays.

Adjusting Display Modes

The Quartz Display Services API provides several functions that adjust the display mode:

If you want to adjust the display mode, you first need to capture the display, as shown in Listing 3-4. The Quartz Display Services function CGDisplaySwitchToMode switches to the display mode returned by the function CGDisplayBestModeForParameters, which in this case, is the best display mode for the main display with a bit depth of 32 bits per pixel and a screen resolution of 1024 by 768 pixels. The display mode that's returned is not always what you asked for. It's the closest mode for the given parameter. The last parameter passed to this function—exactMatch—specifies whether the returned display mode matches exactly. If you don't need this information, you can pass NULL. When your application quits, Quartz Display Services automatically restores the user’s display settings.

Note: Calling CGDisplaySwitchToMode does not guarantee that the display mode switches successfully. Displays have physical limitations that can prevent them from operating in a particular mode.

Listing 3-4  Adjusting the display mode

CGDisplayCapture (kCGDirectMainDisplay ) ;
CGDisplaySwitchToMode (kCGDirectMainDisplay,
            CGDisplayBestModeForParameters (kCGDirectMainDisplay,
                                 32, 1024, 768, NULL) );

Listing 3-5 shows how to switch the main display to a pixel depth of 32 bits per pixel, a resolution of 640 x 480, and a refresh rate of 60 Hz. A detailed explanation for each numbered line of code appears following the listing.

Listing 3-5  Switching the resolution of a display

CFDictionaryRef displayMode ;
CFNumberRef number ;
boolean_t exactMatch ;
 
CGDisplayCapture (kCGDirectMainDisplay); // 1
displayMode =
    CGDisplayBestModeForParametersAndRefreshRate (kCGDirectMainDisplay,
                            32,640,480,60,&exactMatch); // 2
if (exactMatch){        // 3
     CGDisplaySwitchToMode (kCGDirectMainDisplay, displayMode);
}
else {
    // Your code to take appropriate action
}
// Run the event loop.
CGReleaseAllDisplays(); // 4

Here's what the code does:

  1. Captures the main display.

  2. Requests a display mode with a depth of 32 bits per pixel, a resolution 640 x 480, and a refresh rate 60 Hz. The function finds the best match for these parameters.

  3. If there is an exact match, then switches to the display mode.

  4. Before the application quits, releases all displays.

What's Next?

The Quartz Display Services API performs a number of other tasks that are useful when drawing OpenGL to the full screen. “Quartz Display Services and Full-Screen Mode” describes many of them, including enumerating displays and display modes, accessing display properties, fading the display, and programmatically controlling the pointer. You may also want to read “Draw Only When Necessary” to see how to use Quartz Display Services to synchronize drawing with the screen refresh.

See Also

OpenGL sample code projects (ADC Reference Library):



< Previous PageNext Page > Hide TOC


© 2004, 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-06-09)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.