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