Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

macosx_display.m (12067B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 #import "macosx_display.h"
     24 
     25 #include "tr_local.h"
     26 #import "macosx_local.h"
     27 
     28 #import <Foundation/Foundation.h>
     29 #import <IOKit/graphics/IOGraphicsTypes.h>  // for interpreting the kCGDisplayIOFlags element of the display mode
     30 
     31 
     32 NSDictionary *Sys_GetMatchingDisplayMode(qboolean allowStretchedModes)
     33 {
     34     NSArray *displayModes;
     35     NSDictionary *mode;
     36     unsigned int modeIndex, modeCount, bestModeIndex;
     37     int verbose;
     38     cvar_t *cMinFreq, *cMaxFreq;
     39     int minFreq, maxFreq;
     40     unsigned int colorDepth;
     41     
     42     verbose = r_verbose->integer;
     43 
     44     colorDepth = r_colorbits->integer;
     45     if (colorDepth < 16 || !r_fullscreen->integer)
     46         colorDepth = [[glw_state.desktopMode objectForKey: (id)kCGDisplayBitsPerPixel] intValue];
     47 
     48     cMinFreq = ri.Cvar_Get("r_minDisplayRefresh", "0", CVAR_ARCHIVE);
     49     cMaxFreq = ri.Cvar_Get("r_maxDisplayRefresh", "0", CVAR_ARCHIVE);
     50 
     51     if (cMinFreq && cMaxFreq && cMinFreq->integer && cMaxFreq->integer &&
     52         cMinFreq->integer > cMaxFreq->integer) {
     53         ri.Error(ERR_FATAL, "r_minDisplayRefresh must be less than or equal to r_maxDisplayRefresh");
     54     }
     55 
     56     minFreq = cMinFreq ? cMinFreq->integer : 0;
     57     maxFreq = cMaxFreq ? cMaxFreq->integer : 0;
     58     
     59     displayModes = (NSArray *)CGDisplayAvailableModes(glw_state.display);
     60     if (!displayModes) {
     61         ri.Error(ERR_FATAL, "CGDisplayAvailableModes returned NULL -- 0x%0x is an invalid display", glw_state.display);
     62     }
     63     
     64     modeCount = [displayModes count];
     65     if (verbose) {
     66         ri.Printf(PRINT_ALL, "%d modes avaliable\n", modeCount);
     67         ri.Printf(PRINT_ALL, "Current mode is %s\n", [[(id)CGDisplayCurrentMode(glw_state.display) description] cString]);
     68     }
     69     
     70     // Default to the current desktop mode
     71     bestModeIndex = 0xFFFFFFFF;
     72     
     73     for ( modeIndex = 0; modeIndex < modeCount; ++modeIndex ) {
     74         id object;
     75         int refresh;
     76         
     77         mode = [displayModes objectAtIndex: modeIndex];
     78         if (verbose) {
     79             ri.Printf(PRINT_ALL, " mode %d -- %s\n", modeIndex, [[mode description] cString]);
     80         }
     81 
     82         // Make sure we get the right size
     83         object = [mode objectForKey: (id)kCGDisplayWidth];
     84 
     85         if ([[mode objectForKey: (id)kCGDisplayWidth] intValue] != glConfig.vidWidth ||
     86             [[mode objectForKey: (id)kCGDisplayHeight] intValue] != glConfig.vidHeight) {
     87             if (verbose)
     88                 ri.Printf(PRINT_ALL, " -- bad size\n");
     89             continue;
     90         }
     91 
     92         if (!allowStretchedModes) {
     93             if ([[mode objectForKey: (id)kCGDisplayIOFlags] intValue] & kDisplayModeStretchedFlag) {
     94                 if (verbose)
     95                     ri.Printf(PRINT_ALL, " -- stretched modes disallowed\n");
     96                 continue;
     97             }
     98         }
     99 
    100         // Make sure that our frequency restrictions are observed
    101         refresh = [[mode objectForKey: (id)kCGDisplayRefreshRate] intValue];
    102         if (minFreq &&  refresh < minFreq) {
    103             if (verbose)
    104                 ri.Printf(PRINT_ALL, " -- refresh too low\n");
    105             continue;
    106         }
    107 
    108         if (maxFreq && refresh > maxFreq) {
    109             if (verbose)
    110                 ri.Printf(PRINT_ALL, " -- refresh too high\n");
    111             continue;
    112         }
    113 
    114         if ([[mode objectForKey: (id)kCGDisplayBitsPerPixel] intValue] != colorDepth) {
    115             if (verbose)
    116                 ri.Printf(PRINT_ALL, " -- bad depth\n");
    117             continue;
    118         }
    119 
    120         bestModeIndex = modeIndex;
    121         if (verbose)
    122             ri.Printf(PRINT_ALL, " -- OK\n", bestModeIndex);
    123     }
    124 
    125     if (verbose)
    126         ri.Printf(PRINT_ALL, " bestModeIndex = %d\n", bestModeIndex);
    127 
    128     if (bestModeIndex == 0xFFFFFFFF) {
    129         ri.Printf(PRINT_ALL, "No suitable display mode available.\n");
    130         return nil;
    131     }
    132     
    133     return [displayModes objectAtIndex: bestModeIndex];
    134 }
    135 
    136 
    137 #define MAX_DISPLAYS 128
    138 
    139 void Sys_GetGammaTable(glwgamma_t *table)
    140 {
    141     CGTableCount tableSize = 512;
    142     CGDisplayErr err;
    143     
    144     table->tableSize = tableSize;
    145     if (table->red)
    146         free(table->red);
    147     table->red = malloc(tableSize * sizeof(*table->red));
    148     if (table->green)
    149         free(table->green);
    150     table->green = malloc(tableSize * sizeof(*table->green));
    151     if (table->blue)
    152         free(table->blue);
    153     table->blue = malloc(tableSize * sizeof(*table->blue));
    154     
    155     // TJW: We _could_ loop here if we get back the same size as our table, increasing the table size.
    156     err = CGGetDisplayTransferByTable(table->display, tableSize, table->red, table->green, table->blue,
    157 &table->tableSize);
    158     if (err != CGDisplayNoErr) {
    159         Com_Printf("GLimp_Init: CGGetDisplayTransferByTable returned %d.\n", err);
    160         table->tableSize = 0;
    161     }
    162 }
    163 
    164 void Sys_SetGammaTable(glwgamma_t *table)
    165 {
    166 }
    167 
    168 
    169 void Sys_StoreGammaTables()
    170 {
    171     // Store the original gamma for all monitors so that we can fade and unfade them all
    172     CGDirectDisplayID displays[MAX_DISPLAYS];
    173     CGDisplayCount displayIndex;
    174     CGDisplayErr err;
    175 
    176     err = CGGetActiveDisplayList(MAX_DISPLAYS, displays, &glw_state.displayCount);
    177     if (err != CGDisplayNoErr)
    178         Sys_Error("Cannot get display list -- CGGetActiveDisplayList returned %d.\n", err);
    179     
    180     glw_state.originalDisplayGammaTables = calloc(glw_state.displayCount, sizeof(*glw_state.originalDisplayGammaTables));
    181     for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
    182         glwgamma_t *table;
    183 
    184         table = &glw_state.originalDisplayGammaTables[displayIndex];
    185         table->display = displays[displayIndex];
    186         Sys_GetGammaTable(table);
    187     }
    188 }
    189 
    190 
    191 //  This isn't a mathematically correct fade, but we don't care that much.
    192 void Sys_SetScreenFade(glwgamma_t *table, float fraction)
    193 {
    194     CGTableCount tableSize;
    195     CGGammaValue *red, *blue, *green;
    196     CGTableCount gammaIndex;
    197     
    198     if (!glConfig.deviceSupportsGamma)
    199         return;
    200 
    201     if (!(tableSize = table->tableSize))
    202         // we couldn't get the table for this display for some reason
    203         return;
    204     
    205 //    Com_Printf("0x%08x %f\n", table->display, fraction);
    206     
    207     red = glw_state.tempTable.red;
    208     green = glw_state.tempTable.green;
    209     blue = glw_state.tempTable.blue;
    210     if (glw_state.tempTable.tableSize < tableSize) {
    211         glw_state.tempTable.tableSize = tableSize;
    212         red = realloc(red, sizeof(*red) * tableSize);
    213         green = realloc(green, sizeof(*green) * tableSize);
    214         blue = realloc(blue, sizeof(*blue) * tableSize);
    215         glw_state.tempTable.red = red;
    216         glw_state.tempTable.green = green;
    217         glw_state.tempTable.blue = blue;
    218     }
    219 
    220     for (gammaIndex = 0; gammaIndex < table->tableSize; gammaIndex++) {
    221         red[gammaIndex] = table->red[gammaIndex] * fraction;
    222         blue[gammaIndex] = table->blue[gammaIndex] * fraction;
    223         green[gammaIndex] = table->green[gammaIndex] * fraction;
    224     }
    225     
    226     CGSetDisplayTransferByTable(table->display, table->tableSize, red, green, blue);
    227 }
    228 
    229 // Fades all the active displays at the same time.
    230 
    231 #define FADE_DURATION 0.5
    232 void Sys_FadeScreens()
    233 {
    234     CGDisplayCount displayIndex;
    235     int stepIndex;
    236     glwgamma_t *table;
    237     NSTimeInterval start, current;
    238     float time;
    239     
    240     if (!glConfig.deviceSupportsGamma)
    241         return;
    242 
    243     Com_Printf("Fading all displays\n");
    244     
    245     start = [NSDate timeIntervalSinceReferenceDate];
    246     time = 0.0;
    247     while (time != FADE_DURATION) {
    248         current = [NSDate timeIntervalSinceReferenceDate];
    249         time = current - start;
    250         if (time > FADE_DURATION)
    251             time = FADE_DURATION;
    252             
    253         for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {            
    254             table = &glw_state.originalDisplayGammaTables[displayIndex];
    255             Sys_SetScreenFade(table, 1.0 - time / FADE_DURATION);
    256         }
    257     }
    258 }
    259 
    260 void Sys_FadeScreen(CGDirectDisplayID display)
    261 {
    262     CGDisplayCount displayIndex;
    263     glwgamma_t *table;
    264     int stepIndex;
    265     
    266     if (!glConfig.deviceSupportsGamma)
    267         return;
    268 
    269     Com_Printf("Fading display 0x%08x\n", display);
    270 
    271     for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
    272         if (display == glw_state.originalDisplayGammaTables[displayIndex].display) {
    273             NSTimeInterval start, current;
    274             float time;
    275             
    276             start = [NSDate timeIntervalSinceReferenceDate];
    277             time = 0.0;
    278 
    279             table = &glw_state.originalDisplayGammaTables[displayIndex];
    280             while (time != FADE_DURATION) {
    281                 current = [NSDate timeIntervalSinceReferenceDate];
    282                 time = current - start;
    283                 if (time > FADE_DURATION)
    284                     time = FADE_DURATION;
    285 
    286                 Sys_SetScreenFade(table, 1.0 - time / FADE_DURATION);
    287             }
    288             return;
    289         }
    290     }
    291 
    292     Com_Printf("Unable to find display to fade it\n");
    293 }
    294 
    295 void Sys_UnfadeScreens()
    296 {
    297     CGDisplayCount displayIndex;
    298     int stepIndex;
    299     glwgamma_t *table;
    300     NSTimeInterval start, current;
    301     float time;
    302     
    303     if (!glConfig.deviceSupportsGamma)
    304         return;
    305         
    306     Com_Printf("Unfading all displays\n");
    307 
    308     start = [NSDate timeIntervalSinceReferenceDate];
    309     time = 0.0;
    310     while (time != FADE_DURATION) {
    311         current = [NSDate timeIntervalSinceReferenceDate];
    312         time = current - start;
    313         if (time > FADE_DURATION)
    314             time = FADE_DURATION;
    315             
    316         for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {            
    317             table = &glw_state.originalDisplayGammaTables[displayIndex];
    318             Sys_SetScreenFade(table, time / FADE_DURATION);
    319         }
    320     }
    321 }
    322 
    323 void Sys_UnfadeScreen(CGDirectDisplayID display, glwgamma_t *table)
    324 {
    325     CGDisplayCount displayIndex;
    326     int stepIndex;
    327     
    328     if (!glConfig.deviceSupportsGamma)
    329         return;
    330     
    331     Com_Printf("Unfading display 0x%08x\n", display);
    332 
    333     if (table) {
    334         CGTableCount i;
    335         
    336         Com_Printf("Given table:\n");
    337         for (i = 0; i < table->tableSize; i++) {
    338             Com_Printf("  %f %f %f\n", table->red[i], table->blue[i], table->green[i]);
    339         }
    340     }
    341     
    342     // Search for the original gamma table for the display
    343     if (!table) {
    344         for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
    345             if (display == glw_state.originalDisplayGammaTables[displayIndex].display) {
    346                 table = &glw_state.originalDisplayGammaTables[displayIndex];
    347                 break;
    348             }
    349         }
    350     }
    351     
    352     if (table) {
    353         NSTimeInterval start, current;
    354         float time;
    355         
    356         start = [NSDate timeIntervalSinceReferenceDate];
    357         time = 0.0;
    358 
    359         while (time != FADE_DURATION) {
    360             current = [NSDate timeIntervalSinceReferenceDate];
    361             time = current - start;
    362             if (time > FADE_DURATION)
    363                 time = FADE_DURATION;
    364             Sys_SetScreenFade(table, time / FADE_DURATION);
    365         }
    366         return;
    367     }
    368     
    369     Com_Printf("Unable to find display to unfade it\n");
    370 }
    371 
    372 
    373