< Previous PageNext Page > Hide TOC

Quartz Display Services and Full-Screen Mode

The Quartz Display Services API provides functionality that is useful for any Mac OS X application using full-screen mode. In other parts of this programming guide, you've seen how to use Quartz Display Services to obtain exclusive access to the display, query the display, and adjust the resolution, depth, and refresh rate of the display. This appendix shows how to perform some additional tasks that are relevant to full-screen OpenGL applications:

For more information, see Quartz Display Services Reference.

Displays and Display Modes

Quartz Display Services functions allow you to enumerate all displays as well as the supported modes for each display. The Quartz Display Services functions CGDisplaySwitchToMode and CGDisplayBestModeForParameters use on the Core Foundation CFDictionary data type. Each display mode has a dictionary whose key-value pairs you can query. You can use accessor functions to query the properties of the current display mode. You can also use Core Foundation functions to access the dictionary associated with a display mode. See CFDictionary Reference.

You can enumerate the display modes for a display by using its display ID. You can obtain an array of display IDs that correspond to all displays in the system by calling the function CGGetActiveDisplayList. The first display in the list is always the main display. The main display is also represented by the constant kCGDirectMainDisplay.

These functions also obtain an array of display IDs:

Typically you use the functions CGGetDisplaysWithPoint and CGGetDisplaysWithRect when tracking user interactions. You choose which display to capture based on where the user places the pointer. After capturing the display, you can the obtain the supported modes by calling the function CGDisplayAvailableModes.

Listing C-1 shows how to switch the last display in a display list into its first mode and then print the height and width of the display. A detailed explanation for each numbered line of code appears following the listing.

Listing C-1  Switching modes for a display in a list

#define MAX_DISPLAYS 32
 
CGDirectDisplayID lastDisplay, displayArray[MAX_DISPLAYS] ;
CGDisplayCount numDisplays;
 
CFArrayRef displayModeArray;
CFDictionaryRef displayMode;
 
CFNumberRef number;
long height, width;
 
CGGetActiveDisplayList (MAX_DISPLAYS, displayArray, &numDisplays); // 1
lastDisplay = displayArray [numDisplays - 1]; // 2
CGDisplayCapture (lastDisplay); // 3
displayModeArray = CGDisplayAvailableModes (lastDisplay); // 4
displayMode = (CFDictionaryRef) CFArrayGetValueAtIndex (displayModeArray, 0); // 5
CGDisplaySwitchToMode (lastDisplay, displayMode); // 6
/* Run the event loop. */
CGReleaseAllDisplays(); // 7

Here's what the code does:

  1. Gets the array of active displays, which are the ones available for drawing.

  2. Gets the display ID of the last display in the array. The array is zero-based.

  3. Captures the display associated with the last display in the array.

  4. Gets all the display modes for the display.

  5. Gets the first display mode for the display. Recall that the display mode is stored as a CFDictionary object that contains key-value pairs for the attributes of the display mode.

  6. Switches the display mode.

  7. Before the application quits, releases all displays.

Quartz Display Services provides simple accessor functions for many properties of the current display mode. For these properties, you don't need to call CFDictionaryGetValue. Listing C-2 shows how to obtain the properties of the current mode of every display (up to 32) in the system.

Listing C-2  Getting display properties

#define MAX_DISPLAYS 32
 
CGDirectDisplayID displayArray [MAX_DISPLAYS];
CGDisplayCount numDisplays;
CFNumberRef number;
CFBoolean booleanValue;
long    height, width, refresh, mode,
        bpp, bps, spp, rowBytes, gui, ioflags;
int     i;
 
CGGetActiveDisplayList (MAX_DISPLAYS, displayArray, &numDisplays); // 1
printf ("Displays installed: %d\n", numDisplays); // 2
for(i = 0; i < numDisplays; i++) { // 3
    width = CGDisplayPixelsWide (displayArray[i]);
    height = CGDisplayPixelsHigh (displayArray[i]);
    bpp = CGDisplayBitsPerPixel (displayArray[i]);
    bps = CGDisplayBitsPerSample (displayArray[i]);
    spp = CGDisplaySamplesPerPixel (displayArray[i]);
    rowBytes = CGDisplayBytesPerRow (displayArray[i]);
    number = CFDictionaryGetValue (CGDisplayCurrentMode (displayArray[i]),
                                    kCGDisplayMode);
    CFNumberGetValue (number, kCFNumberLongType, &mode);
    number = CFDictionaryGetValue (CGDisplayCurrentMode (displayArray[i]),
                                    kCGDisplayRefreshRate);
    CFNumberGetValue (number, kCFNumberLongType, &refresh);
    booleanValue = CFDictionaryGetValue (CGDisplayCurrentMode(displayArray[i]),
                                kCGDisplayModeUsableForDesktopGUI);
    CFNumberGetValue (number, kCFNumberLongType, &gui);
    number = CFDictionaryGetValue (CGDisplayCurrentMode (displayArray[i]),
                                kCGDisplayIOFlags);
    CFNumberGetValue (number, kCFNumberLongType, &ioflags);
}

Here's what the code does:

  1. Gets the array of displays.

  2. Prints out the number of displays.

  3. Gets the properties for the current mode of each display. Note that Quartz Display Services provides several functions that obtain properties. For information about the current display mode, you must use the function CFDictionaryGetValue, along with the appropriate key, to retrieve a value from the display mode dictionary returned by the function CGDisplayCurrentMode.

Fading the Display

Fading a display ensures a smooth transition when entering full-screen mode, especially when switching display modes. There are two options for fading displays:

Listing C-3  Fading all displays connected to the system

CGDisplayFadeReservationToken token;
CGDisplayErr err;
 
err = CGAcquireDisplayFadeReservation (kCGMaxDisplayReservationInterval, &token); // 1
if (err == kCGErrorSuccess)
{
    err = CGDisplayFade (token, 0.5, kCGDisplayBlendNormal,
                kCGDisplayBlendSolidColor, 0, 0, 0, true); // 2
    // Your code to change the display mode and
    // set the full-screen context.
    err = CGDisplayFade (token, 0.5, kCGDisplayBlendSolidColor,
          kCGDisplayBlendNormal, 0, 0, 0, true); // 3
    err = CGReleaseDisplayFadeReservation (token); // 4
}

Here's what the code does:

  1. Reserves the display hardware for the maximum amount of time allowable. Your application must perform this step before it can fade the displays. During this time, your application has exclusive rights to use the fade hardware.

  2. Fades displays to black over a duration of 0.5 seconds

  3. Performs a fade-in for all displays, from black to normal, over a duration of 0.5 seconds

  4. Releases the fade reservation and invalidates the fade token.

When you adjust the gamma value to fade a display, you can't assume that the maximum gamma value is 1.0 because the user might have specified a different maximum value in System Preferences. You need to retrieve the current settings and scale them so that they range from 0 to 1. Listing C-3 shows how to fade the main display to black and back. Note that the code uses a loop is used to obtain a smooth fade. A more robust technique is to use a timer to ensure a fixed fade duration on different systems. A detailed explanation for each numbered line of code appears following the listing.

Listing C-4  Fading a single display on a system with multiple displays

#define kMyFadeTime    1.0
#define kMyFadeSteps    100
#define kMyFadeInterval    ( kMyFadeTime/(double) kMyFadeSteps)
 
int step;
double fadeValue ;
CGGammaValue    redMin, redMax, redGamma,
                greenMin, greenMax, greenGamma,
                blueMin, blueMax, blueGamma;
 
CGGetDisplayTransferByFormula (kCGDirectMainDisplay,
            &redMin, &redMax, &redGamma,
            &greenMin, &greenMax, &greenGamma,
            &blueMin, &blueMax, &blueGamma ); // 1
 
for ( step = 0; step < kMyFadeSteps; ++step ) {
    fadeValue = 1.0 - (step * kMyFadeInterval);
    CGSetDisplayTransferByFormula (kCGDirectMainDisplay,
            redMin, fadeValue*redMax, redGamma,
            greenMin, fadeValue*greenMax, greenGamma,
            blueMin, fadeValue*blueMax, blueGamma ); // 2
    usleep( (useconds_t)(1000000.0 * kMyFadeInterval) ); // 3
}
// Your code to change the display mode and
// attach the context to a full-screen drawable object.
// Run the event loop.
for ( step = 0; step < kMyFadeSteps; ++step ) {
    fadeValue = (step * kMyFadeInterval);
    CGSetDisplayTransferByFormula( kCGDirectMainDisplay,
            redMin, fadeValue*redMax, redGamma,
            greenMin, fadeValue*greenMax, greenGamma,
            blueMin, fadeValue*blueMax, blueGamma ); // 4
    usleep( (useconds_t)(1000000.0 * kMyFadeInterval) );  // 5
}
CGDisplayRestoreColorSyncSettings() // 6

Here's what the code does:

  1. Gets the current coefficients of the gamma transfer formula for a display as the starting gamma values.

  2. Fades from the current gamma by setting the color gamma function for the display, specified as the coefficients of the gamma transfer formula. Starts with the current gamma (multiplying by a factor of 1.0) and ends with black (multiplying by a factor of 0.0).

  3. Suspends processing for a short interval. You either need to use a timer or insert a short delay to achieve a fade effect because the call to change the display gamma returns within 100 microseconds or so, and the actual gamma is applied asynchronously during the next vertical blanking period. Without the delay, you'll get what appears as an instantaneous switch to black rather than a fade effect.

  4. Fade from black (multiplying by a factor of 0.0) back to original gamma (multiplying by a factor of 1.0).

  5. Suspends processing for a short interval to achieve a smooth fade-in effect.

  6. Finds and applies all ColorSync settings for all attached displays, restoring the gamma tables to the values in the user's ColorSync display profile. It's a good idea to call this function because the operation performed by the function CGSetDisplayTransferByFormula can't reproduce precisely the color correction data for all displays, particularly LCD panels.

Controlling the Pointer

When you use full-screen mode, you may want to hide the pointer, programatically move the pointer, or disassociate mouse movement from pointer position. To hide or show the pointer, use the functions CGDisplayHideCursor and CGDisplayShowCursor. These functions control the pointer visibility on all displays.

Quartz Display Services provides a convenient function for disassociating mouse movement from pointer position while an application is in the foreground. By passing false to the function CGAssociateMouseAndMouseCursorPosition, you can prevent mouse movement from changing the pointer position. Pass true to reverse the effect. You should also hide the menu bar because clicking it can cause the pointer to become visible again, even after capturing the display.

You can move the pointer programatically by calling the function CGDisplayMoveCursorToPoint. This function takes two parameters, a display ID and a point. The location of the point is relative to the display origin (the upper-left corner of the display).

Listing C-5 shows how you would hide and move the cursor on the main display, disassociate the cursor from mouse movement, and restore the cursor and mouse when you are done.

Listing C-5  Controlling the pointer programmatically

CGDisplayHideCursor (kCGDirectMainDisplay); //Hide cursor
CGDisplayMoveCursorToPoint (kCGDirectMainDisplay,CGPointZero); //Place at display origin
CGAssociateMouseAndMouseCursorPosition (FALSE);
// Perform your application's main loop.
//In the mouse movement notification function, get the motion deltas
CGAssociateMouseAndMouseCursorPosition (TRUE);
CGDisplayShowCursor (kCGDirectMainDisplay);

See Also

Quartz Display Services Reference which describes the application programming interface that configures and controls the display hardware.



< 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.