373 lines
12 KiB
Objective-C
Executable file
373 lines
12 KiB
Objective-C
Executable file
/*
|
|
===========================================================================
|
|
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 <Foundation/Foundation.h>
|
|
#import <IOKit/graphics/IOGraphicsTypes.h> // 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");
|
|
}
|
|
|
|
|
|
|