mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-30 16:11:11 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
835 lines
27 KiB
C++
835 lines
27 KiB
C++
/*
|
||
===========================================================================
|
||
|
||
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 <http://www.gnu.org/licenses/>.
|
||
|
||
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 <list>
|
||
#include <set>
|
||
|
||
#include "sys/platform.h"
|
||
#include "framework/CVarSystem.h"
|
||
#include "sys/osx/PickMonitor.h"
|
||
|
||
#include "PreferencesDialog.h"
|
||
|
||
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); // <20><><EFBFBD><EFBFBD>(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<Fixed> 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<Res> 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<Res>::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<Fixed>* 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<Fixed>* 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<Fixed>::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<Fixed>* inList, Fixed inFrequency)
|
||
{
|
||
SInt32 index = 3; // skip over the "Auto" and divider ine
|
||
for (std::list<Fixed>::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<Fixed>::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;
|
||
}
|
||
|
||
|
||
//------------------------------------------------------------------------------------
|
||
// <20> 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;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|