/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #import "macosx_display.h" #include "tr_local.h" #import "macosx_local.h" #import #import // for interpreting the kCGDisplayIOFlags element of the display mode NSDictionary *Sys_GetMatchingDisplayMode(qboolean allowStretchedModes) { NSArray *displayModes; NSDictionary *mode; unsigned int modeIndex, modeCount, bestModeIndex; int verbose; cvar_t *cMinFreq, *cMaxFreq; int minFreq, maxFreq; unsigned int colorDepth; verbose = r_verbose->integer; colorDepth = r_colorbits->integer; if (colorDepth < 16 || !r_fullscreen->integer) colorDepth = [[glw_state.desktopMode objectForKey: (id)kCGDisplayBitsPerPixel] intValue]; cMinFreq = ri.Cvar_Get("r_minDisplayRefresh", "0", CVAR_ARCHIVE); cMaxFreq = ri.Cvar_Get("r_maxDisplayRefresh", "0", CVAR_ARCHIVE); if (cMinFreq && cMaxFreq && cMinFreq->integer && cMaxFreq->integer && cMinFreq->integer > cMaxFreq->integer) { ri.Error(ERR_FATAL, "r_minDisplayRefresh must be less than or equal to r_maxDisplayRefresh"); } minFreq = cMinFreq ? cMinFreq->integer : 0; maxFreq = cMaxFreq ? cMaxFreq->integer : 0; displayModes = (NSArray *)CGDisplayAvailableModes(glw_state.display); if (!displayModes) { ri.Error(ERR_FATAL, "CGDisplayAvailableModes returned NULL -- 0x%0x is an invalid display", glw_state.display); } modeCount = [displayModes count]; if (verbose) { ri.Printf(PRINT_ALL, "%d modes avaliable\n", modeCount); ri.Printf(PRINT_ALL, "Current mode is %s\n", [[(id)CGDisplayCurrentMode(glw_state.display) description] cString]); } // Default to the current desktop mode bestModeIndex = 0xFFFFFFFF; for ( modeIndex = 0; modeIndex < modeCount; ++modeIndex ) { id object; int refresh; mode = [displayModes objectAtIndex: modeIndex]; if (verbose) { ri.Printf(PRINT_ALL, " mode %d -- %s\n", modeIndex, [[mode description] cString]); } // Make sure we get the right size object = [mode objectForKey: (id)kCGDisplayWidth]; if ([[mode objectForKey: (id)kCGDisplayWidth] intValue] != glConfig.vidWidth || [[mode objectForKey: (id)kCGDisplayHeight] intValue] != glConfig.vidHeight) { if (verbose) ri.Printf(PRINT_ALL, " -- bad size\n"); continue; } if (!allowStretchedModes) { if ([[mode objectForKey: (id)kCGDisplayIOFlags] intValue] & kDisplayModeStretchedFlag) { if (verbose) ri.Printf(PRINT_ALL, " -- stretched modes disallowed\n"); continue; } } // Make sure that our frequency restrictions are observed refresh = [[mode objectForKey: (id)kCGDisplayRefreshRate] intValue]; if (minFreq && refresh < minFreq) { if (verbose) ri.Printf(PRINT_ALL, " -- refresh too low\n"); continue; } if (maxFreq && refresh > maxFreq) { if (verbose) ri.Printf(PRINT_ALL, " -- refresh too high\n"); continue; } if ([[mode objectForKey: (id)kCGDisplayBitsPerPixel] intValue] != colorDepth) { if (verbose) ri.Printf(PRINT_ALL, " -- bad depth\n"); continue; } bestModeIndex = modeIndex; if (verbose) ri.Printf(PRINT_ALL, " -- OK\n", bestModeIndex); } if (verbose) ri.Printf(PRINT_ALL, " bestModeIndex = %d\n", bestModeIndex); if (bestModeIndex == 0xFFFFFFFF) { ri.Printf(PRINT_ALL, "No suitable display mode available.\n"); return nil; } return [displayModes objectAtIndex: bestModeIndex]; } #define MAX_DISPLAYS 128 void Sys_GetGammaTable(glwgamma_t *table) { CGTableCount tableSize = 512; CGDisplayErr err; table->tableSize = tableSize; if (table->red) free(table->red); table->red = malloc(tableSize * sizeof(*table->red)); if (table->green) free(table->green); table->green = malloc(tableSize * sizeof(*table->green)); if (table->blue) free(table->blue); table->blue = malloc(tableSize * sizeof(*table->blue)); // TJW: We _could_ loop here if we get back the same size as our table, increasing the table size. err = CGGetDisplayTransferByTable(table->display, tableSize, table->red, table->green, table->blue, &table->tableSize); if (err != CGDisplayNoErr) { Com_Printf("GLimp_Init: CGGetDisplayTransferByTable returned %d.\n", err); table->tableSize = 0; } } void Sys_SetGammaTable(glwgamma_t *table) { } void Sys_StoreGammaTables() { // Store the original gamma for all monitors so that we can fade and unfade them all CGDirectDisplayID displays[MAX_DISPLAYS]; CGDisplayCount displayIndex; CGDisplayErr err; err = CGGetActiveDisplayList(MAX_DISPLAYS, displays, &glw_state.displayCount); if (err != CGDisplayNoErr) Sys_Error("Cannot get display list -- CGGetActiveDisplayList returned %d.\n", err); glw_state.originalDisplayGammaTables = calloc(glw_state.displayCount, sizeof(*glw_state.originalDisplayGammaTables)); for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) { glwgamma_t *table; table = &glw_state.originalDisplayGammaTables[displayIndex]; table->display = displays[displayIndex]; Sys_GetGammaTable(table); } } // This isn't a mathematically correct fade, but we don't care that much. void Sys_SetScreenFade(glwgamma_t *table, float fraction) { CGTableCount tableSize; CGGammaValue *red, *blue, *green; CGTableCount gammaIndex; if (!glConfig.deviceSupportsGamma) return; if (!(tableSize = table->tableSize)) // we couldn't get the table for this display for some reason return; // Com_Printf("0x%08x %f\n", table->display, fraction); red = glw_state.tempTable.red; green = glw_state.tempTable.green; blue = glw_state.tempTable.blue; if (glw_state.tempTable.tableSize < tableSize) { glw_state.tempTable.tableSize = tableSize; red = realloc(red, sizeof(*red) * tableSize); green = realloc(green, sizeof(*green) * tableSize); blue = realloc(blue, sizeof(*blue) * tableSize); glw_state.tempTable.red = red; glw_state.tempTable.green = green; glw_state.tempTable.blue = blue; } for (gammaIndex = 0; gammaIndex < table->tableSize; gammaIndex++) { red[gammaIndex] = table->red[gammaIndex] * fraction; blue[gammaIndex] = table->blue[gammaIndex] * fraction; green[gammaIndex] = table->green[gammaIndex] * fraction; } CGSetDisplayTransferByTable(table->display, table->tableSize, red, green, blue); } // Fades all the active displays at the same time. #define FADE_DURATION 0.5 void Sys_FadeScreens() { CGDisplayCount displayIndex; int stepIndex; glwgamma_t *table; NSTimeInterval start, current; float time; if (!glConfig.deviceSupportsGamma) return; Com_Printf("Fading all displays\n"); start = [NSDate timeIntervalSinceReferenceDate]; time = 0.0; while (time != FADE_DURATION) { current = [NSDate timeIntervalSinceReferenceDate]; time = current - start; if (time > FADE_DURATION) time = FADE_DURATION; for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) { table = &glw_state.originalDisplayGammaTables[displayIndex]; Sys_SetScreenFade(table, 1.0 - time / FADE_DURATION); } } } void Sys_FadeScreen(CGDirectDisplayID display) { CGDisplayCount displayIndex; glwgamma_t *table; int stepIndex; if (!glConfig.deviceSupportsGamma) return; Com_Printf("Fading display 0x%08x\n", display); for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) { if (display == glw_state.originalDisplayGammaTables[displayIndex].display) { NSTimeInterval start, current; float time; start = [NSDate timeIntervalSinceReferenceDate]; time = 0.0; table = &glw_state.originalDisplayGammaTables[displayIndex]; while (time != FADE_DURATION) { current = [NSDate timeIntervalSinceReferenceDate]; time = current - start; if (time > FADE_DURATION) time = FADE_DURATION; Sys_SetScreenFade(table, 1.0 - time / FADE_DURATION); } return; } } Com_Printf("Unable to find display to fade it\n"); } void Sys_UnfadeScreens() { CGDisplayCount displayIndex; int stepIndex; glwgamma_t *table; NSTimeInterval start, current; float time; if (!glConfig.deviceSupportsGamma) return; Com_Printf("Unfading all displays\n"); start = [NSDate timeIntervalSinceReferenceDate]; time = 0.0; while (time != FADE_DURATION) { current = [NSDate timeIntervalSinceReferenceDate]; time = current - start; if (time > FADE_DURATION) time = FADE_DURATION; for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) { table = &glw_state.originalDisplayGammaTables[displayIndex]; Sys_SetScreenFade(table, time / FADE_DURATION); } } } void Sys_UnfadeScreen(CGDirectDisplayID display, glwgamma_t *table) { CGDisplayCount displayIndex; int stepIndex; if (!glConfig.deviceSupportsGamma) return; Com_Printf("Unfading display 0x%08x\n", display); if (table) { CGTableCount i; Com_Printf("Given table:\n"); for (i = 0; i < table->tableSize; i++) { Com_Printf(" %f %f %f\n", table->red[i], table->blue[i], table->green[i]); } } // Search for the original gamma table for the display if (!table) { for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) { if (display == glw_state.originalDisplayGammaTables[displayIndex].display) { table = &glw_state.originalDisplayGammaTables[displayIndex]; break; } } } if (table) { NSTimeInterval start, current; float time; start = [NSDate timeIntervalSinceReferenceDate]; time = 0.0; while (time != FADE_DURATION) { current = [NSDate timeIntervalSinceReferenceDate]; time = current - start; if (time > FADE_DURATION) time = FADE_DURATION; Sys_SetScreenFade(table, time / FADE_DURATION); } return; } Com_Printf("Unable to find display to unfade it\n"); }