/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 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 3 of the License, or
(at your option) any later version.
Doom 3 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 Doom 3 Source Code. If not, see .
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#include
#include "PreferencesDialog.h"
#include "PickMonitor.h"
#include
#include
static idCVar r_stretched( "r_stretched", "0", CVAR_ARCHIVE | CVAR_BOOL, "Used stretched resolution" );
#define kPref_PrefsDialogAlways CFSTR("PrefsDialogAlways")
#define kPref_PrefsDialogOpenAL CFSTR("UseOpenAL")
#ifndef kAppCreator
#define kAppCreator 'DOM3' // Creator type
#endif
const UInt32 kRes_Stretched = (1 << 0); // set if the resolution is a stretched mode (kCGDisplayModeIsStretched)
const UInt32 kRes_Safe = (1 << 1); // ¥¥¥Ê(currently unused) set if the resolution is safe (kCGDisplayModeIsSafeForHardware)
// Data to be presented and edited in the prefs dialog
struct PrefInfo
{
// prefs values
GameDisplayMode prefGameDisplayMode;
CGDirectDisplayID prefDisplayID;
int prefWidth;
int prefHeight;
int prefDepth;
Fixed prefFrequency;
UInt32 prefResFlags;
Boolean prefAlways;
Boolean prefOpenAL;
bool okPressed; // Set to true if the user pressed the OK button
// The following are private data passed from GameDisplayPreferencesDialog() to it's command handler.
WindowRef window;
ControlRef fullscreenBtn;
ControlRef inAWindowBtn;
ControlRef resolutionPopup;
ControlRef refreshRatePopup;
ControlRef chooseMonitorsBtn;
ControlRef alwaysBtn;
ControlRef openALBtn;
ValidModeCallbackProc callback; // To validate display modes
bool multiMonitor; // Does user have multiple monitors
std::list refreshRates; // List of refresh rates available for the selected monitor
SInt32 freqMenuIndex;
};
bool R_GetModeInfo( int *width, int *height, int mode );
static int GetScreenIndexForDisplayID( CGDirectDisplayID inDisplayID ) {
unsigned int i;
OSErr err;
int r_screen = -1;
CGDisplayCount count;
err = CGGetActiveDisplayList(0, NULL, &count);
if (noErr == err) {
CGDirectDisplayID displays[count];
err = CGGetActiveDisplayList(count, displays, &count);
if (noErr == err) {
for ( i = 0; i < count; i++)
if (displays[i] == inDisplayID)
r_screen = i;
}
}
return r_screen;
}
static CGDirectDisplayID GetDisplayIDForScreenIndex( int inScreenIndex ) {
OSErr err;
int r_screen = -1;
CGDisplayCount count;
err = CGGetActiveDisplayList(0, NULL, &count);
if (noErr == err) {
CGDirectDisplayID displays[count];
err = CGGetActiveDisplayList(count, displays, &count);
if (noErr == err) {
if ( inScreenIndex >= 0 && inScreenIndex <= count )
return displays[inScreenIndex];
}
}
return (CGDirectDisplayID)r_screen;
}
void Sys_DoPreferences( void ) {
// An NSKeyDown event is not fired if the user holds down Cmd during startup.
// Cmd is treated purely as a modifier. To capture the user
// holding down Cmd, you would need to override NSApplication's
// keydown handler. That's overkill for a single check at
// startup, use the Carbon GetKeys approach.
unsigned char km[16];
const int kMacKeyCodeCommand = 0x37;
KeyMap *keymap = (KeyMap*)&km;
GetKeys(*keymap);
Boolean prefAways, keyFound, useOpenAL;
prefAways = CFPreferencesGetAppBooleanValue ( kPref_PrefsDialogAlways, kCFPreferencesCurrentApplication, &keyFound );
bool fAlways = prefAways && keyFound;
if ( fAlways || ( km[kMacKeyCodeCommand>>3] >> ( kMacKeyCodeCommand & 7 ) ) & 1 ) {
GameDisplayInfo info;
info.mode = cvarSystem->GetCVarBool( "r_fullscreen" ) ? kFullScreen : kWindow;
info.displayID = GetDisplayIDForScreenIndex( cvarSystem->GetCVarInteger( "r_screen" ) );
int w = 800, h = 600;
R_GetModeInfo( &w, &h, cvarSystem->GetCVarInteger( "r_mode" ) );
info.width = w;
info.height = h;
info.depth = 32;
info.frequency = cvarSystem->GetCVarInteger( "r_maxDisplayRefresh" );
info.windowLoc.x = 0;
info.windowLoc.y = 0;
info.flags = 0;
info.resFlags = 0;
if ( r_stretched.GetBool() )
info.resFlags |= kRes_Stretched;
WindowRef prefWindow;
if ( CreateGameDisplayPreferencesDialog( &info, &prefWindow ) == noErr ) {
if ( RunGameDisplayPreferencesDialog( &info, prefWindow ) == noErr ) {
cvarSystem->SetCVarBool( "r_fullscreen", info.mode == kFullScreen );
int i = 0;
int r_mode = -1;
while ( r_mode == -1 && R_GetModeInfo( &w, &h, i ) ) {
if ( w == info.width && h == info.height )
r_mode = i;
i++;
}
cvarSystem->SetCVarInteger( "r_mode", r_mode );
if ( r_mode == -1 ) {
cvarSystem->SetCVarInteger( "r_customWidth", info.width );
cvarSystem->SetCVarInteger( "r_customHeight", info.height );
}
float r = (float) info.width / (float) info.height;
if ( r > 1.7f )
cvarSystem->SetCVarInteger( "r_aspectRatio", 1 ); // 16:9
else if ( r > 1.55f )
cvarSystem->SetCVarInteger( "r_aspectRatio", 2 ); // 16:10
else
cvarSystem->SetCVarInteger( "r_aspectRatio", 0 ); // 4:3
r_stretched.SetBool( info.resFlags & kRes_Stretched );
cvarSystem->SetCVarInteger( "r_screen", GetScreenIndexForDisplayID( info.displayID ) );
cvarSystem->SetCVarInteger( "r_minDisplayRefresh", (int)FixedToFloat( info.frequency ) );
cvarSystem->SetCVarInteger( "r_maxDisplayRefresh", (int)FixedToFloat( info.frequency ) );
}
else {
Sys_Quit();
}
}
}
useOpenAL = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogOpenAL, kCFPreferencesCurrentApplication, &keyFound);
if ( keyFound && useOpenAL ) {
cvarSystem->SetCVarInteger( "com_asyncSound", 1 );
cvarSystem->SetCVarInteger( "s_useOpenAL", 1 );
}
else {
cvarSystem->SetCVarInteger( "com_asyncSound", 2 );
cvarSystem->SetCVarInteger( "s_useOpenAL", 0 );
}
}
#define EnablePopupMenuItem(inControl,inMenuItem) EnableMenuItem(GetControlPopupMenuRef(inControl),inMenuItem)
#define DisablePopupMenuItem(inControl,inMenuItem) DisableMenuItem(GetControlPopupMenuRef(inControl),inMenuItem)
#define IsPopupMenuItemEnabled(inControl,inMenuItem) IsMenuItemEnabled(GetControlPopupMenuRef(inControl),inMenuItem)
// Command IDs used in the NIB file
enum
{
kCmdFullscreen = 'Full',
kCmdInAWindow = 'Wind',
kCmdResolution = 'Reso',
kCmdRefreshRate = 'Refr',
kCmdChooseMonitors = 'Moni',
};
// Control IDs used in the NIB file
static const ControlID kFullscreenBtn = { 'PREF', 1 };
static const ControlID kInAWindowBtn = { 'PREF', 2 };
static const ControlID kResolutionPopup = { 'PREF', 3 };
static const ControlID kRefreshRatePopup = { 'PREF', 4 };
static const ControlID kChooseMonitorsBtn = { 'PREF', 5 };
static const ControlID kAlwaysBtn = { 'PREF', 6 };
static const ControlID kOpenALBtn = { 'PREF', 7 };
struct Res
{
int width;
int height;
int depth;
UInt32 resFlags;
};
static bool operator< (const Res& a, const Res& b)
{
if (a.width == b.width)
{
if (a.height == b.height)
{
if (a.resFlags == b.resFlags)
{
return (a.depth < b.depth);
}
return (a.resFlags < b.resFlags);
}
return (a.height < b.height);
}
return (a.width < b.width);
}
inline Res MakeRes(int width, int height, int depth)
{
Res temp = { width, height, depth, 0 };
return temp;
}
inline Res MakeRes(int width, int height, int depth, UInt32 resFlags)
{
Res temp = { width, height, depth, resFlags };
return temp;
}
static bool ValidDisplayID (CGDirectDisplayID inDisplayID)
{
unsigned int i;
CGDisplayErr err;
CGDisplayCount count;
err = CGGetActiveDisplayList(0, NULL, &count);
if (noErr == err)
{
CGDirectDisplayID displays[count];
err = CGGetActiveDisplayList(count, displays, &count);
if (noErr == err)
{
for ( i = 0; i < count; i++)
if (displays[i] == inDisplayID)
return true;
}
}
return false;
}
static int BuildResolutionList(CGDirectDisplayID inDisplayID, Res *ioList, ValidModeCallbackProc inCallback)
{
std::set modes;
int i, total = 0;
if (inDisplayID == (CGDirectDisplayID)-1) // special case, not associated with any display
{
Res stdModes[] = { { 640, 480 }, { 800, 600 }, { 1024, 768 }, { 1152, 768 },
{ 1280, 854 }, { 1280, 960 }, { 1280, 1024 }, { 1440, 900 } };
total = sizeof(stdModes) / sizeof(Res);
for (i = 0; i < total; i++)
{
if (inCallback == NULL || inCallback(inDisplayID, stdModes[i].width, stdModes[i].height, 32, 0))
modes.insert( MakeRes(stdModes[i].width, stdModes[i].height, 32) );
}
}
else
{
CGDirectDisplayID displayID = inDisplayID ? inDisplayID : kCGDirectMainDisplay;
CFArrayRef modeArrayRef = CGDisplayAvailableModes(displayID);
CFIndex numModes = CFArrayGetCount(modeArrayRef);
for (i = 0; i < numModes; i++)
{
CFDictionaryRef modeRef = (CFDictionaryRef)CFArrayGetValueAtIndex(modeArrayRef, i);
long value = 0;
CFNumberRef valueRef;
Boolean success;
valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayBitsPerPixel);
success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
int depth = value;
if (depth != 32) continue;
valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayWidth);
success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
int width = value;
valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayHeight);
success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
int height = value;
UInt32 resFlags = 0;
CFBooleanRef boolRef;
if (CFDictionaryGetValueIfPresent (modeRef, kCGDisplayModeIsStretched, (const void **)&boolRef))
if (CFBooleanGetValue (boolRef))
resFlags |= kRes_Stretched;
if (inCallback)
success = inCallback(displayID, width, height, depth, 0);
else
success = true;
if (success)
modes.insert(MakeRes(width, height, depth, resFlags));
}
}
total = modes.size();
if (ioList)
{
std::set::iterator it = modes.begin();
for (i = 0; it != modes.end(); i++)
ioList[i] = *it++;
}
return total;
}
static void BuildRefreshRates(CGDirectDisplayID inDisplayID, int inWidth, int inHeight, std::list* inList, ValidModeCallbackProc inCallback)
{
CGDirectDisplayID displayID = inDisplayID ? inDisplayID : kCGDirectMainDisplay;
CFArrayRef modeArrayRef = CGDisplayAvailableModes(displayID);
CFIndex numModes = CFArrayGetCount(modeArrayRef);
inList->clear();
for (int i = 0; i < numModes; i++)
{
CFDictionaryRef modeRef = (CFDictionaryRef)CFArrayGetValueAtIndex(modeArrayRef, i);
long value = 0;
CFNumberRef valueRef;
Boolean success;
valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayBitsPerPixel);
success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
int depth = value;
if (depth != 32) continue;
valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayWidth);
success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
int width = value;
valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayHeight);
success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
int height = value;
if (width == inWidth && height == inHeight)
{
double freqDouble;
valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayRefreshRate);
success = CFNumberGetValue(valueRef, kCFNumberDoubleType, &freqDouble);
Fixed freq = FloatToFixed(freqDouble);
if (inCallback)
success = inCallback(displayID, width, height, depth, freq);
else
success = true;
if (success)
inList->push_back(freq);
}
}
// Disallow 0, which we reserve to mean "automatic"
inList->remove(0);
inList->sort();
// Remove duplicates - yes they can occur.
inList->unique();
}
static void BuildRefreshPopupButton(ControlRef inControl, std::list* inList)
{
MenuRef menu = GetControlPopupMenuRef(inControl);
assert(menu);
if (!menu) return;
// The menu has two permanent items - "Auto" & a divider line. Delete everything else.
DeleteMenuItems(menu, 3, CountMenuItems(menu)-2);
for (std::list::const_iterator iter = inList->begin(); iter != inList->end(); ++iter)
{
float value = FixedToFloat(*iter);
CFStringRef menuString = CFStringCreateWithFormat (kCFAllocatorDefault, 0, CFSTR("%g Hz"), value);
InsertMenuItemTextWithCFString(menu, menuString, CountMenuItems(menu), 0, 0);
}
SetControlMaximum(inControl, CountMenuItems(menu));
}
static SInt32 FindRefreshPopupMenuItem(std::list* inList, Fixed inFrequency)
{
SInt32 index = 3; // skip over the "Auto" and divider ine
for (std::list::const_iterator iter = inList->begin(); iter != inList->end(); ++iter)
{
if (*iter == inFrequency)
return index;
index++;
}
return 1; // Return the "Automatic" item if we didn't find a match
}
static void BuildResolutionPopupButton(ControlRef inControl, CGDirectDisplayID inDisplayID, ValidModeCallbackProc inCallback)
{
// Get the list of valid resolutions
int count = BuildResolutionList(inDisplayID, NULL, inCallback);
Res resList[count];
BuildResolutionList(inDisplayID, resList, inCallback);
// Clear the menu
MenuRef menu = GetControlPopupMenuRef(inControl);
assert(menu);
if (!menu) return;
DeleteMenuItems(menu, 1, CountMenuItems(menu));
OSStatus err;
while (count--)
{
CFStringRef menuString = CFStringCreateWithFormat (kCFAllocatorDefault, 0, CFSTR("%d x %d %@"),
resList[count].width, resList[count].height, (resList[count].resFlags & kRes_Stretched) ? CFSTR("(Stretched)") : CFSTR(""));
InsertMenuItemTextWithCFString (menu, menuString, 0, 0, 0);
err = SetMenuItemProperty (menu, 1, kAppCreator, 'Res ', sizeof(resList[count]), &resList[count]);
}
SetControlMaximum(inControl, CountMenuItems(menu));
}
static void GetResolutionFromPopupMenuItem(ControlRef inControl, MenuItemIndex inItem, int *outX, int *outY, int *outDepth, UInt32 *outResFlags)
{
MenuRef menu = GetControlPopupMenuRef(inControl);
Res res;
OSStatus err;
err = GetMenuItemProperty (menu, inItem, kAppCreator, 'Res ', sizeof(res), NULL, &res);
if (!err)
{
*outX = res.width;
*outY = res.height;
*outResFlags = res.resFlags;
*outDepth = 32;
}
}
static void AdjustResolutionPopupMenu(ControlRef inControl, CGDirectDisplayID inDisplayID, bool isFullscreen, int& screenwidth, int& screenheight, int& screendepth, UInt32& screenResFlags)
{
int screenX = INT_MAX, screenY = INT_MAX;
// In windowed mode, you have to disable resolutions that are larger than the current screen size
if (!isFullscreen)
{
screenX = (int)CGDisplayPixelsWide(inDisplayID);
screenY = (int)CGDisplayPixelsHigh(inDisplayID);
}
MenuRef menu = GetControlPopupMenuRef(inControl);
int resX, resY, depth;
UInt32 resFlags;
int count = CountMenuItems(menu);
int item;
for( item = 1; item <= count; item++)
{
GetResolutionFromPopupMenuItem(inControl, item, &resX, &resY, &depth, &resFlags);
if (screenX < resX || screenY < resY)
DisablePopupMenuItem(inControl, item);
else
EnablePopupMenuItem(inControl, item);
if (resX == screenwidth && resY == screenheight && depth == screendepth && resFlags == screenResFlags)
SetControlValue(inControl, item);
}
// If we just disabled the current item, then choose something else.
if (!IsPopupMenuItemEnabled(inControl, GetControlValue (inControl)))
{
for(item = 1; item <= count; item++)
{
if (IsPopupMenuItemEnabled(inControl, item))
{
SetControlValue(inControl, item);
GetResolutionFromPopupMenuItem(inControl, item, &screenwidth, &screenheight, &screendepth, &screenResFlags);
break;
}
}
}
}
static void AdjustDisplayControls(PrefInfo *prefInfo)
{
// Build new resolution popup and select appropriate resolution
if ((prefInfo->prefGameDisplayMode != kFullScreen))
{
BuildResolutionPopupButton(prefInfo->resolutionPopup, (CGDirectDisplayID)-1, prefInfo->callback);
if (prefInfo->multiMonitor)
EnableControl(prefInfo->chooseMonitorsBtn);
}
else
{
BuildResolutionPopupButton(prefInfo->resolutionPopup, prefInfo->prefDisplayID, prefInfo->callback);
if (prefInfo->multiMonitor)
EnableControl(prefInfo->chooseMonitorsBtn);
}
AdjustResolutionPopupMenu(prefInfo->resolutionPopup, prefInfo->prefDisplayID,
prefInfo->prefGameDisplayMode == kFullScreen,
prefInfo->prefWidth, prefInfo->prefHeight, prefInfo->prefDepth, prefInfo->prefResFlags);
// Build new refresh popup and select appropriate rate
BuildRefreshRates(prefInfo->prefDisplayID, prefInfo->prefWidth, prefInfo->prefHeight,
&prefInfo->refreshRates, prefInfo->callback);
BuildRefreshPopupButton(prefInfo->refreshRatePopup, &prefInfo->refreshRates);
if (prefInfo->refreshRates.size() == 0)
{ // No refresh rates, so pick Auto
prefInfo->freqMenuIndex = 1;
prefInfo->prefFrequency = 0;
}
else
{
prefInfo->freqMenuIndex = FindRefreshPopupMenuItem(&prefInfo->refreshRates, prefInfo->prefFrequency);
if (prefInfo->freqMenuIndex == 1)
prefInfo->prefFrequency = 0; // just in case FindRefreshPopupMenuItem didn't find prefInfo->prefFrequency
}
SetControlValue (prefInfo->refreshRatePopup, prefInfo->freqMenuIndex);
// Disable refresh rate if NOT fullscreen
if ((prefInfo->prefGameDisplayMode != kFullScreen) || (prefInfo->refreshRates.size() == 0))
DisableControl (prefInfo->refreshRatePopup);
else
EnableControl (prefInfo->refreshRatePopup);
}
static pascal OSStatus PrefHandler( EventHandlerCallRef, EventRef inEvent, void* inUserData )
{
HICommand cmd;
OSStatus result = eventNotHandledErr;
PrefInfo* prefInfo = (PrefInfo*)inUserData;
// The direct object for a 'process commmand' event is the HICommand.
// Extract it here and switch off the command ID.
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd );
switch ( cmd.commandID )
{
case kHICommandOK:
prefInfo->okPressed = true;
prefInfo->prefAlways = GetControlValue (prefInfo->alwaysBtn);
prefInfo->prefOpenAL = GetControlValue (prefInfo->openALBtn);
CFPreferencesSetAppValue (kPref_PrefsDialogAlways,
prefInfo->prefAlways ? kCFBooleanTrue : kCFBooleanFalse,
kCFPreferencesCurrentApplication);
CFPreferencesSetAppValue (kPref_PrefsDialogOpenAL,
prefInfo->prefOpenAL ? kCFBooleanTrue : kCFBooleanFalse,
kCFPreferencesCurrentApplication);
CFPreferencesAppSynchronize (kCFPreferencesCurrentApplication);
QuitAppModalLoopForWindow( prefInfo->window );
result = noErr;
break;
case kHICommandCancel:
prefInfo->okPressed = false;
QuitAppModalLoopForWindow( prefInfo->window );
result = noErr;
break;
case kCmdFullscreen:
case kCmdInAWindow:
if (cmd.commandID == kCmdFullscreen)
prefInfo->prefGameDisplayMode = kFullScreen;
else
prefInfo->prefGameDisplayMode = kWindow;
SetControlValue (prefInfo->fullscreenBtn, prefInfo->prefGameDisplayMode == kFullScreen);
SetControlValue (prefInfo->inAWindowBtn, 1 - (prefInfo->prefGameDisplayMode == kFullScreen));
if (prefInfo->prefGameDisplayMode == kFullScreen)
EnableControl (prefInfo->refreshRatePopup);
else
DisableControl (prefInfo->refreshRatePopup);
if (prefInfo->multiMonitor)
EnableControl (prefInfo->chooseMonitorsBtn);
else
DisableControl (prefInfo->chooseMonitorsBtn);
// Adjust resolutions, refresh rates
AdjustDisplayControls(prefInfo);
result = noErr;
break;
case kCmdChooseMonitors:
{
PickMonitor((DisplayIDType*)&prefInfo->prefDisplayID, prefInfo->window);
// Adjust resolutions, refresh rates for potentially new display ID
AdjustDisplayControls(prefInfo);
break;
}
case kCmdResolution:
{
// Pick a new resolution
int item = GetControlValue(prefInfo->resolutionPopup);
GetResolutionFromPopupMenuItem(prefInfo->resolutionPopup, item, &prefInfo->prefWidth, &prefInfo->prefHeight, &prefInfo->prefDepth, &prefInfo->prefResFlags);
// Adjust refresh menu
BuildRefreshRates(prefInfo->prefDisplayID, prefInfo->prefWidth, prefInfo->prefHeight, &prefInfo->refreshRates, prefInfo->callback);
BuildRefreshPopupButton(prefInfo->refreshRatePopup, &prefInfo->refreshRates);
prefInfo->freqMenuIndex = FindRefreshPopupMenuItem(&prefInfo->refreshRates, prefInfo->prefFrequency);
if (prefInfo->freqMenuIndex == 1)
prefInfo->prefFrequency = 0; // just in case FindRefreshPopupMenuItem didn't find prefInfo->prefFrequency
SetControlValue (prefInfo->refreshRatePopup, prefInfo->freqMenuIndex);
// Disable refresh rate if NOT fullscreen
if ((prefInfo->prefGameDisplayMode != kFullScreen) || (prefInfo->refreshRates.size() == 0))
DisableControl (prefInfo->refreshRatePopup);
else
EnableControl (prefInfo->refreshRatePopup);
break;
}
case kCmdRefreshRate:
{
// Keep prefInfo->prefFrequency updated for the other controls to reference
prefInfo->freqMenuIndex = GetControlValue (prefInfo->refreshRatePopup);
if (prefInfo->freqMenuIndex == 1)
prefInfo->prefFrequency = 0;
else
{
std::list::const_iterator iter = prefInfo->refreshRates.begin();
for (int i = 0; i < prefInfo->freqMenuIndex-3; i++)
iter++;
prefInfo->prefFrequency = *iter;
}
break;
}
}
return result;
}
static DEFINE_ONE_SHOT_HANDLER_GETTER(PrefHandler)
OSStatus CreateGameDisplayPreferencesDialog(const GameDisplayInfo *inGDInfo,
WindowRef *outWindow, ValidModeCallbackProc inCallback)
{
OSStatus err = noErr;
// Build up a structure to pass to the window handler we are about
// to install. We store the window itself, as well as the original
// states of our settings. We use this to revert if the user clicks
// the cancel button.
static PrefInfo prefInfo;
prefInfo.prefGameDisplayMode = inGDInfo->mode;
prefInfo.prefDisplayID = inGDInfo->displayID;
prefInfo.prefWidth = inGDInfo->width;
prefInfo.prefHeight = inGDInfo->height;
prefInfo.prefDepth = inGDInfo->depth;
prefInfo.prefFrequency = inGDInfo->frequency;
prefInfo.prefResFlags = inGDInfo->resFlags;
prefInfo.window = NULL;
prefInfo.okPressed = false;
Boolean result;
Boolean keyFound;
result = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogAlways, kCFPreferencesCurrentApplication, &keyFound);
prefInfo.prefAlways = result && keyFound;
result = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogOpenAL, kCFPreferencesCurrentApplication, &keyFound);
prefInfo.prefOpenAL = result && keyFound;
prefInfo.callback = inCallback;
// If DoPreferences is called at the start of the game, prefInfo.prefDisplayID needs to be checked
// to see if it is still a valid display ID.
if (!ValidDisplayID(prefInfo.prefDisplayID))
prefInfo.prefDisplayID = kCGDirectMainDisplay; // revert to main
// Fetch the dialog
IBNibRef aslNib;
CFBundleRef theBundle = CFBundleGetMainBundle();
err = CreateNibReferenceWithCFBundle(theBundle, CFSTR("ASLCore"), &aslNib);
err = ::CreateWindowFromNib(aslNib, CFSTR( "Preferences" ), &prefInfo.window );
if (err != noErr)
return err;
SetWRefCon(prefInfo.window, (long)&prefInfo);
// Locate all the controls
GetControlByID( prefInfo.window, &kFullscreenBtn, &prefInfo.fullscreenBtn ); assert(prefInfo.fullscreenBtn);
GetControlByID( prefInfo.window, &kInAWindowBtn, &prefInfo.inAWindowBtn ); assert(prefInfo.inAWindowBtn);
GetControlByID( prefInfo.window, &kResolutionPopup, &prefInfo.resolutionPopup ); assert(prefInfo.resolutionPopup);
GetControlByID( prefInfo.window, &kRefreshRatePopup, &prefInfo.refreshRatePopup ); assert(prefInfo.refreshRatePopup);
GetControlByID( prefInfo.window, &kChooseMonitorsBtn, &prefInfo.chooseMonitorsBtn ); assert(prefInfo.chooseMonitorsBtn);
GetControlByID( prefInfo.window, &kAlwaysBtn, &prefInfo.alwaysBtn ); assert(prefInfo.alwaysBtn);
GetControlByID( prefInfo.window, &kOpenALBtn, &prefInfo.openALBtn ); assert(prefInfo.openALBtn);
// Disable the "choose monitor" button if we've only got one to pick from
prefInfo.multiMonitor = CanUserPickMonitor();
if (!prefInfo.multiMonitor)
{
DisableControl (prefInfo.chooseMonitorsBtn);
prefInfo.prefDisplayID = 0;
}
// Prepare the resolutions and refresh rates popup menus
AdjustDisplayControls(&prefInfo);
// Set up the controls
SetControlValue (prefInfo.refreshRatePopup, prefInfo.freqMenuIndex);
SetControlValue (prefInfo.fullscreenBtn, prefInfo.prefGameDisplayMode == kFullScreen);
SetControlValue (prefInfo.inAWindowBtn, prefInfo.prefGameDisplayMode == kWindow);
SetControlValue (prefInfo.alwaysBtn, prefInfo.prefAlways);
SetControlValue (prefInfo.openALBtn, prefInfo.prefOpenAL);
// Create our UPP and install the handler.
EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess };
EventHandlerUPP handler = GetPrefHandlerUPP();
InstallWindowEventHandler( prefInfo.window, handler, 1, &cmdEvent, &prefInfo, NULL );
// Position and show the window
RepositionWindow( prefInfo.window, NULL, kWindowAlertPositionOnMainScreen );
if (outWindow)
*outWindow = prefInfo.window;
return err;
}
//------------------------------------------------------------------------------------
// ¥ RunGameDisplayPreferencesDialog
//------------------------------------------------------------------------------------
// Runs the Mac-specific preferences dialog.
OSStatus RunGameDisplayPreferencesDialog(GameDisplayInfo *outGDInfo, WindowRef inWindow)
{
PrefInfo *prefInfo = (PrefInfo*)GetWRefCon(inWindow);
ShowWindow( inWindow );
// Now we run modally. We will remain here until the PrefHandler
// calls QuitAppModalLoopForWindow if the user clicks OK or
// Cancel.
RunAppModalLoopForWindow( inWindow );
// OK, we're done. Dispose of our window.
// TODO: Are we supposed to uninstall event handlers?
DisposeWindow( inWindow );
// Return settings to caller
if (prefInfo->okPressed)
{
outGDInfo->mode = prefInfo->prefGameDisplayMode;
outGDInfo->width = prefInfo->prefWidth;
outGDInfo->height = prefInfo->prefHeight;
outGDInfo->depth = prefInfo->prefDepth;
outGDInfo->frequency = prefInfo->prefFrequency;
outGDInfo->resFlags = prefInfo->prefResFlags;
outGDInfo->displayID = prefInfo->prefDisplayID;
}
return prefInfo->okPressed ? noErr : userCanceledErr;
}