2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
24 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.
26 ===========================================================================
30 void Sys_DoPreferences( void ) {}
32 #include "../../idlib/precompiled.h"
33 #include <Carbon/Carbon.h>
34 #include "PreferencesDialog.h"
35 #include "PickMonitor.h"
39 static idCVar r_stretched( "r_stretched", "0", CVAR_ARCHIVE | CVAR_BOOL, "Used stretched resolution" );
41 #define kPref_PrefsDialogAlways CFSTR("PrefsDialogAlways")
42 #define kPref_PrefsDialogOpenAL CFSTR("UseOpenAL")
45 #define kAppCreator 'DOM3' // Creator type
48 const UInt32 kRes_Stretched = (1 << 0); // set if the resolution is a stretched mode (kCGDisplayModeIsStretched)
49 const UInt32 kRes_Safe = (1 << 1); // ¥¥¥Ê(currently unused) set if the resolution is safe (kCGDisplayModeIsSafeForHardware)
51 // Data to be presented and edited in the prefs dialog
55 GameDisplayMode prefGameDisplayMode;
56 CGDirectDisplayID prefDisplayID;
65 bool okPressed; // Set to true if the user pressed the OK button
67 // The following are private data passed from GameDisplayPreferencesDialog() to it's command handler.
69 ControlRef fullscreenBtn;
70 ControlRef inAWindowBtn;
71 ControlRef resolutionPopup;
72 ControlRef refreshRatePopup;
73 ControlRef chooseMonitorsBtn;
77 ValidModeCallbackProc callback; // To validate display modes
79 bool multiMonitor; // Does user have multiple monitors
80 std::list<Fixed> refreshRates; // List of refresh rates available for the selected monitor
87 bool R_GetModeInfo( int *width, int *height, int mode );
89 static int GetScreenIndexForDisplayID( CGDirectDisplayID inDisplayID ) {
95 err = CGGetActiveDisplayList(0, NULL, &count);
97 CGDirectDisplayID displays[count];
98 err = CGGetActiveDisplayList(count, displays, &count);
100 for ( i = 0; i < count; i++)
101 if (displays[i] == inDisplayID)
108 static CGDirectDisplayID GetDisplayIDForScreenIndex( int inScreenIndex ) {
111 CGDisplayCount count;
113 err = CGGetActiveDisplayList(0, NULL, &count);
115 CGDirectDisplayID displays[count];
116 err = CGGetActiveDisplayList(count, displays, &count);
118 if ( inScreenIndex >= 0 && inScreenIndex <= count )
119 return displays[inScreenIndex];
122 return (CGDirectDisplayID)r_screen;
127 void Sys_DoPreferences( void ) {
129 // An NSKeyDown event is not fired if the user holds down Cmd during startup.
130 // Cmd is treated purely as a modifier. To capture the user
131 // holding down Cmd, you would need to override NSApplication's
132 // keydown handler. That's overkill for a single check at
133 // startup, use the Carbon GetKeys approach.
134 unsigned char km[16];
135 const int kMacKeyCodeCommand = 0x37;
136 KeyMap *keymap = (KeyMap*)&km;
139 Boolean prefAways, keyFound, useOpenAL;
140 prefAways = CFPreferencesGetAppBooleanValue ( kPref_PrefsDialogAlways, kCFPreferencesCurrentApplication, &keyFound );
141 bool fAlways = prefAways && keyFound;
143 if ( fAlways || ( km[kMacKeyCodeCommand>>3] >> ( kMacKeyCodeCommand & 7 ) ) & 1 ) {
144 GameDisplayInfo info;
145 info.mode = cvarSystem->GetCVarBool( "r_fullscreen" ) ? kFullScreen : kWindow;
146 info.displayID = GetDisplayIDForScreenIndex( cvarSystem->GetCVarInteger( "r_screen" ) );
148 int w = 800, h = 600;
149 R_GetModeInfo( &w, &h, cvarSystem->GetCVarInteger( "r_mode" ) );
153 info.frequency = cvarSystem->GetCVarInteger( "r_maxDisplayRefresh" );
154 info.windowLoc.x = 0;
155 info.windowLoc.y = 0;
158 if ( r_stretched.GetBool() )
159 info.resFlags |= kRes_Stretched;
161 WindowRef prefWindow;
162 if ( CreateGameDisplayPreferencesDialog( &info, &prefWindow ) == noErr ) {
163 if ( RunGameDisplayPreferencesDialog( &info, prefWindow ) == noErr ) {
164 cvarSystem->SetCVarBool( "r_fullscreen", info.mode == kFullScreen );
168 while ( r_mode == -1 && R_GetModeInfo( &w, &h, i ) ) {
169 if ( w == info.width && h == info.height )
173 cvarSystem->SetCVarInteger( "r_mode", r_mode );
174 if ( r_mode == -1 ) {
175 cvarSystem->SetCVarInteger( "r_customWidth", info.width );
176 cvarSystem->SetCVarInteger( "r_customHeight", info.height );
179 float r = (float) info.width / (float) info.height;
181 cvarSystem->SetCVarInteger( "r_aspectRatio", 1 ); // 16:9
182 else if ( r > 1.55f )
183 cvarSystem->SetCVarInteger( "r_aspectRatio", 2 ); // 16:10
185 cvarSystem->SetCVarInteger( "r_aspectRatio", 0 ); // 4:3
187 r_stretched.SetBool( info.resFlags & kRes_Stretched );
188 cvarSystem->SetCVarInteger( "r_screen", GetScreenIndexForDisplayID( info.displayID ) );
189 cvarSystem->SetCVarInteger( "r_minDisplayRefresh", (int)FixedToFloat( info.frequency ) );
190 cvarSystem->SetCVarInteger( "r_maxDisplayRefresh", (int)FixedToFloat( info.frequency ) );
197 useOpenAL = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogOpenAL, kCFPreferencesCurrentApplication, &keyFound);
198 if ( keyFound && useOpenAL ) {
199 cvarSystem->SetCVarInteger( "com_asyncSound", 1 );
200 cvarSystem->SetCVarInteger( "s_useOpenAL", 1 );
203 cvarSystem->SetCVarInteger( "com_asyncSound", 2 );
204 cvarSystem->SetCVarInteger( "s_useOpenAL", 0 );
211 #define EnablePopupMenuItem(inControl,inMenuItem) EnableMenuItem(GetControlPopupMenuRef(inControl),inMenuItem)
212 #define DisablePopupMenuItem(inControl,inMenuItem) DisableMenuItem(GetControlPopupMenuRef(inControl),inMenuItem)
213 #define IsPopupMenuItemEnabled(inControl,inMenuItem) IsMenuItemEnabled(GetControlPopupMenuRef(inControl),inMenuItem)
215 // Command IDs used in the NIB file
218 kCmdFullscreen = 'Full',
219 kCmdInAWindow = 'Wind',
220 kCmdResolution = 'Reso',
221 kCmdRefreshRate = 'Refr',
222 kCmdChooseMonitors = 'Moni',
225 // Control IDs used in the NIB file
226 static const ControlID kFullscreenBtn = { 'PREF', 1 };
227 static const ControlID kInAWindowBtn = { 'PREF', 2 };
228 static const ControlID kResolutionPopup = { 'PREF', 3 };
229 static const ControlID kRefreshRatePopup = { 'PREF', 4 };
230 static const ControlID kChooseMonitorsBtn = { 'PREF', 5 };
231 static const ControlID kAlwaysBtn = { 'PREF', 6 };
232 static const ControlID kOpenALBtn = { 'PREF', 7 };
242 static bool operator< (const Res& a, const Res& b)
244 if (a.width == b.width)
246 if (a.height == b.height)
248 if (a.resFlags == b.resFlags)
250 return (a.depth < b.depth);
252 return (a.resFlags < b.resFlags);
254 return (a.height < b.height);
256 return (a.width < b.width);
259 inline Res MakeRes(int width, int height, int depth)
261 Res temp = { width, height, depth, 0 };
265 inline Res MakeRes(int width, int height, int depth, UInt32 resFlags)
267 Res temp = { width, height, depth, resFlags };
271 static bool ValidDisplayID (CGDirectDisplayID inDisplayID)
275 CGDisplayCount count;
277 err = CGGetActiveDisplayList(0, NULL, &count);
280 CGDirectDisplayID displays[count];
281 err = CGGetActiveDisplayList(count, displays, &count);
284 for ( i = 0; i < count; i++)
285 if (displays[i] == inDisplayID)
292 static int BuildResolutionList(CGDirectDisplayID inDisplayID, Res *ioList, ValidModeCallbackProc inCallback)
297 if (inDisplayID == (CGDirectDisplayID)-1) // special case, not associated with any display
299 Res stdModes[] = { { 640, 480 }, { 800, 600 }, { 1024, 768 }, { 1152, 768 },
300 { 1280, 854 }, { 1280, 960 }, { 1280, 1024 }, { 1440, 900 } };
301 total = sizeof(stdModes) / sizeof(Res);
302 for (i = 0; i < total; i++)
304 if (inCallback == NULL || inCallback(inDisplayID, stdModes[i].width, stdModes[i].height, 32, 0))
305 modes.insert( MakeRes(stdModes[i].width, stdModes[i].height, 32) );
310 CGDirectDisplayID displayID = inDisplayID ? inDisplayID : kCGDirectMainDisplay;
311 CFArrayRef modeArrayRef = CGDisplayAvailableModes(displayID);
312 CFIndex numModes = CFArrayGetCount(modeArrayRef);
314 for (i = 0; i < numModes; i++)
316 CFDictionaryRef modeRef = (CFDictionaryRef)CFArrayGetValueAtIndex(modeArrayRef, i);
319 CFNumberRef valueRef;
322 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayBitsPerPixel);
323 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
325 if (depth != 32) continue;
327 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayWidth);
328 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
331 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayHeight);
332 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
336 CFBooleanRef boolRef;
337 if (CFDictionaryGetValueIfPresent (modeRef, kCGDisplayModeIsStretched, (const void **)&boolRef))
338 if (CFBooleanGetValue (boolRef))
339 resFlags |= kRes_Stretched;
343 success = inCallback(displayID, width, height, depth, 0);
348 modes.insert(MakeRes(width, height, depth, resFlags));
352 total = modes.size();
356 std::set<Res>::iterator it = modes.begin();
357 for (i = 0; it != modes.end(); i++)
367 static void BuildRefreshRates(CGDirectDisplayID inDisplayID, int inWidth, int inHeight, std::list<Fixed>* inList, ValidModeCallbackProc inCallback)
369 CGDirectDisplayID displayID = inDisplayID ? inDisplayID : kCGDirectMainDisplay;
371 CFArrayRef modeArrayRef = CGDisplayAvailableModes(displayID);
372 CFIndex numModes = CFArrayGetCount(modeArrayRef);
376 for (int i = 0; i < numModes; i++)
378 CFDictionaryRef modeRef = (CFDictionaryRef)CFArrayGetValueAtIndex(modeArrayRef, i);
381 CFNumberRef valueRef;
384 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayBitsPerPixel);
385 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
387 if (depth != 32) continue;
389 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayWidth);
390 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
393 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayHeight);
394 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
397 if (width == inWidth && height == inHeight)
400 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayRefreshRate);
401 success = CFNumberGetValue(valueRef, kCFNumberDoubleType, &freqDouble);
402 Fixed freq = FloatToFixed(freqDouble);
404 success = inCallback(displayID, width, height, depth, freq);
408 inList->push_back(freq);
412 // Disallow 0, which we reserve to mean "automatic"
417 // Remove duplicates - yes they can occur.
421 static void BuildRefreshPopupButton(ControlRef inControl, std::list<Fixed>* inList)
423 MenuRef menu = GetControlPopupMenuRef(inControl);
427 // The menu has two permanent items - "Auto" & a divider line. Delete everything else.
428 DeleteMenuItems(menu, 3, CountMenuItems(menu)-2);
430 for (std::list<Fixed>::const_iterator iter = inList->begin(); iter != inList->end(); ++iter)
432 float value = FixedToFloat(*iter);
433 CFStringRef menuString = CFStringCreateWithFormat (kCFAllocatorDefault, 0, CFSTR("%g Hz"), value);
434 InsertMenuItemTextWithCFString(menu, menuString, CountMenuItems(menu), 0, 0);
437 SetControlMaximum(inControl, CountMenuItems(menu));
440 static SInt32 FindRefreshPopupMenuItem(std::list<Fixed>* inList, Fixed inFrequency)
442 SInt32 index = 3; // skip over the "Auto" and divider ine
443 for (std::list<Fixed>::const_iterator iter = inList->begin(); iter != inList->end(); ++iter)
445 if (*iter == inFrequency)
449 return 1; // Return the "Automatic" item if we didn't find a match
452 static void BuildResolutionPopupButton(ControlRef inControl, CGDirectDisplayID inDisplayID, ValidModeCallbackProc inCallback)
454 // Get the list of valid resolutions
455 int count = BuildResolutionList(inDisplayID, NULL, inCallback);
457 BuildResolutionList(inDisplayID, resList, inCallback);
460 MenuRef menu = GetControlPopupMenuRef(inControl);
463 DeleteMenuItems(menu, 1, CountMenuItems(menu));
469 CFStringRef menuString = CFStringCreateWithFormat (kCFAllocatorDefault, 0, CFSTR("%d x %d %@"),
470 resList[count].width, resList[count].height, (resList[count].resFlags & kRes_Stretched) ? CFSTR("(Stretched)") : CFSTR(""));
471 InsertMenuItemTextWithCFString (menu, menuString, 0, 0, 0);
472 err = SetMenuItemProperty (menu, 1, kAppCreator, 'Res ', sizeof(resList[count]), &resList[count]);
475 SetControlMaximum(inControl, CountMenuItems(menu));
478 static void GetResolutionFromPopupMenuItem(ControlRef inControl, MenuItemIndex inItem, int *outX, int *outY, int *outDepth, UInt32 *outResFlags)
480 MenuRef menu = GetControlPopupMenuRef(inControl);
484 err = GetMenuItemProperty (menu, inItem, kAppCreator, 'Res ', sizeof(res), NULL, &res);
489 *outResFlags = res.resFlags;
494 static void AdjustResolutionPopupMenu(ControlRef inControl, CGDirectDisplayID inDisplayID, bool isFullscreen, int& screenwidth, int& screenheight, int& screendepth, UInt32& screenResFlags)
496 int screenX = INT_MAX, screenY = INT_MAX;
498 // In windowed mode, you have to disable resolutions that are larger than the current screen size
501 screenX = (int)CGDisplayPixelsWide(inDisplayID);
502 screenY = (int)CGDisplayPixelsHigh(inDisplayID);
505 MenuRef menu = GetControlPopupMenuRef(inControl);
506 int resX, resY, depth;
508 int count = CountMenuItems(menu);
511 for( item = 1; item <= count; item++)
513 GetResolutionFromPopupMenuItem(inControl, item, &resX, &resY, &depth, &resFlags);
515 if (screenX < resX || screenY < resY)
516 DisablePopupMenuItem(inControl, item);
518 EnablePopupMenuItem(inControl, item);
520 if (resX == screenwidth && resY == screenheight && depth == screendepth && resFlags == screenResFlags)
521 SetControlValue(inControl, item);
524 // If we just disabled the current item, then choose something else.
525 if (!IsPopupMenuItemEnabled(inControl, GetControlValue (inControl)))
527 for(item = 1; item <= count; item++)
529 if (IsPopupMenuItemEnabled(inControl, item))
531 SetControlValue(inControl, item);
532 GetResolutionFromPopupMenuItem(inControl, item, &screenwidth, &screenheight, &screendepth, &screenResFlags);
539 static void AdjustDisplayControls(PrefInfo *prefInfo)
541 // Build new resolution popup and select appropriate resolution
542 if ((prefInfo->prefGameDisplayMode != kFullScreen))
544 BuildResolutionPopupButton(prefInfo->resolutionPopup, (CGDirectDisplayID)-1, prefInfo->callback);
545 if (prefInfo->multiMonitor)
546 EnableControl(prefInfo->chooseMonitorsBtn);
550 BuildResolutionPopupButton(prefInfo->resolutionPopup, prefInfo->prefDisplayID, prefInfo->callback);
551 if (prefInfo->multiMonitor)
552 EnableControl(prefInfo->chooseMonitorsBtn);
554 AdjustResolutionPopupMenu(prefInfo->resolutionPopup, prefInfo->prefDisplayID,
555 prefInfo->prefGameDisplayMode == kFullScreen,
556 prefInfo->prefWidth, prefInfo->prefHeight, prefInfo->prefDepth, prefInfo->prefResFlags);
558 // Build new refresh popup and select appropriate rate
559 BuildRefreshRates(prefInfo->prefDisplayID, prefInfo->prefWidth, prefInfo->prefHeight,
560 &prefInfo->refreshRates, prefInfo->callback);
561 BuildRefreshPopupButton(prefInfo->refreshRatePopup, &prefInfo->refreshRates);
563 if (prefInfo->refreshRates.size() == 0)
564 { // No refresh rates, so pick Auto
565 prefInfo->freqMenuIndex = 1;
566 prefInfo->prefFrequency = 0;
570 prefInfo->freqMenuIndex = FindRefreshPopupMenuItem(&prefInfo->refreshRates, prefInfo->prefFrequency);
571 if (prefInfo->freqMenuIndex == 1)
572 prefInfo->prefFrequency = 0; // just in case FindRefreshPopupMenuItem didn't find prefInfo->prefFrequency
574 SetControlValue (prefInfo->refreshRatePopup, prefInfo->freqMenuIndex);
576 // Disable refresh rate if NOT fullscreen
577 if ((prefInfo->prefGameDisplayMode != kFullScreen) || (prefInfo->refreshRates.size() == 0))
578 DisableControl (prefInfo->refreshRatePopup);
580 EnableControl (prefInfo->refreshRatePopup);
585 static pascal OSStatus PrefHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* inUserData )
587 #pragma unused( inHandler )
590 OSStatus result = eventNotHandledErr;
591 PrefInfo* prefInfo = (PrefInfo*)inUserData;
593 // The direct object for a 'process commmand' event is the HICommand.
594 // Extract it here and switch off the command ID.
596 GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd );
598 switch ( cmd.commandID )
602 prefInfo->okPressed = true;
604 prefInfo->prefAlways = GetControlValue (prefInfo->alwaysBtn);
605 prefInfo->prefOpenAL = GetControlValue (prefInfo->openALBtn);
607 CFPreferencesSetAppValue (kPref_PrefsDialogAlways,
608 prefInfo->prefAlways ? kCFBooleanTrue : kCFBooleanFalse,
609 kCFPreferencesCurrentApplication);
611 CFPreferencesSetAppValue (kPref_PrefsDialogOpenAL,
612 prefInfo->prefOpenAL ? kCFBooleanTrue : kCFBooleanFalse,
613 kCFPreferencesCurrentApplication);
615 CFPreferencesAppSynchronize (kCFPreferencesCurrentApplication);
617 QuitAppModalLoopForWindow( prefInfo->window );
621 case kHICommandCancel:
623 prefInfo->okPressed = false;
625 QuitAppModalLoopForWindow( prefInfo->window );
631 if (cmd.commandID == kCmdFullscreen)
632 prefInfo->prefGameDisplayMode = kFullScreen;
634 prefInfo->prefGameDisplayMode = kWindow;
635 SetControlValue (prefInfo->fullscreenBtn, prefInfo->prefGameDisplayMode == kFullScreen);
636 SetControlValue (prefInfo->inAWindowBtn, 1 - (prefInfo->prefGameDisplayMode == kFullScreen));
637 if (prefInfo->prefGameDisplayMode == kFullScreen)
638 EnableControl (prefInfo->refreshRatePopup);
640 DisableControl (prefInfo->refreshRatePopup);
641 if (prefInfo->multiMonitor)
642 EnableControl (prefInfo->chooseMonitorsBtn);
644 DisableControl (prefInfo->chooseMonitorsBtn);
646 // Adjust resolutions, refresh rates
647 AdjustDisplayControls(prefInfo);
652 case kCmdChooseMonitors:
654 PickMonitor((DisplayIDType*)&prefInfo->prefDisplayID, prefInfo->window);
655 // Adjust resolutions, refresh rates for potentially new display ID
656 AdjustDisplayControls(prefInfo);
662 // Pick a new resolution
663 int item = GetControlValue(prefInfo->resolutionPopup);
664 GetResolutionFromPopupMenuItem(prefInfo->resolutionPopup, item, &prefInfo->prefWidth, &prefInfo->prefHeight, &prefInfo->prefDepth, &prefInfo->prefResFlags);
666 // Adjust refresh menu
667 BuildRefreshRates(prefInfo->prefDisplayID, prefInfo->prefWidth, prefInfo->prefHeight, &prefInfo->refreshRates, prefInfo->callback);
668 BuildRefreshPopupButton(prefInfo->refreshRatePopup, &prefInfo->refreshRates);
669 prefInfo->freqMenuIndex = FindRefreshPopupMenuItem(&prefInfo->refreshRates, prefInfo->prefFrequency);
670 if (prefInfo->freqMenuIndex == 1)
671 prefInfo->prefFrequency = 0; // just in case FindRefreshPopupMenuItem didn't find prefInfo->prefFrequency
672 SetControlValue (prefInfo->refreshRatePopup, prefInfo->freqMenuIndex);
674 // Disable refresh rate if NOT fullscreen
675 if ((prefInfo->prefGameDisplayMode != kFullScreen) || (prefInfo->refreshRates.size() == 0))
676 DisableControl (prefInfo->refreshRatePopup);
678 EnableControl (prefInfo->refreshRatePopup);
683 case kCmdRefreshRate:
685 // Keep prefInfo->prefFrequency updated for the other controls to reference
686 prefInfo->freqMenuIndex = GetControlValue (prefInfo->refreshRatePopup);
687 if (prefInfo->freqMenuIndex == 1)
688 prefInfo->prefFrequency = 0;
691 std::list<Fixed>::const_iterator iter = prefInfo->refreshRates.begin();
692 for (int i = 0; i < prefInfo->freqMenuIndex-3; i++)
694 prefInfo->prefFrequency = *iter;
706 static DEFINE_ONE_SHOT_HANDLER_GETTER(PrefHandler)
708 OSStatus CreateGameDisplayPreferencesDialog(const GameDisplayInfo *inGDInfo,
709 WindowRef *outWindow, ValidModeCallbackProc inCallback)
711 OSStatus err = noErr;
713 // Build up a structure to pass to the window handler we are about
714 // to install. We store the window itself, as well as the original
715 // states of our settings. We use this to revert if the user clicks
716 // the cancel button.
718 static PrefInfo prefInfo;
720 prefInfo.prefGameDisplayMode = inGDInfo->mode;
721 prefInfo.prefDisplayID = inGDInfo->displayID;
722 prefInfo.prefWidth = inGDInfo->width;
723 prefInfo.prefHeight = inGDInfo->height;
724 prefInfo.prefDepth = inGDInfo->depth;
725 prefInfo.prefFrequency = inGDInfo->frequency;
726 prefInfo.prefResFlags = inGDInfo->resFlags;
727 prefInfo.window = NULL;
728 prefInfo.okPressed = false;
732 result = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogAlways, kCFPreferencesCurrentApplication, &keyFound);
733 prefInfo.prefAlways = result && keyFound;
734 result = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogOpenAL, kCFPreferencesCurrentApplication, &keyFound);
735 prefInfo.prefOpenAL = result && keyFound;
737 prefInfo.callback = inCallback;
739 // If DoPreferences is called at the start of the game, prefInfo.prefDisplayID needs to be checked
740 // to see if it is still a valid display ID.
742 if (!ValidDisplayID(prefInfo.prefDisplayID))
743 prefInfo.prefDisplayID = kCGDirectMainDisplay; // revert to main
748 CFBundleRef theBundle = CFBundleGetMainBundle();
749 err = CreateNibReferenceWithCFBundle(theBundle, CFSTR("ASLCore"), &aslNib);
750 err = ::CreateWindowFromNib(aslNib, CFSTR( "Preferences" ), &prefInfo.window );
753 SetWRefCon(prefInfo.window, (long)&prefInfo);
755 // Locate all the controls
757 GetControlByID( prefInfo.window, &kFullscreenBtn, &prefInfo.fullscreenBtn ); assert(prefInfo.fullscreenBtn);
758 GetControlByID( prefInfo.window, &kInAWindowBtn, &prefInfo.inAWindowBtn ); assert(prefInfo.inAWindowBtn);
759 GetControlByID( prefInfo.window, &kResolutionPopup, &prefInfo.resolutionPopup ); assert(prefInfo.resolutionPopup);
760 GetControlByID( prefInfo.window, &kRefreshRatePopup, &prefInfo.refreshRatePopup ); assert(prefInfo.refreshRatePopup);
761 GetControlByID( prefInfo.window, &kChooseMonitorsBtn, &prefInfo.chooseMonitorsBtn ); assert(prefInfo.chooseMonitorsBtn);
762 GetControlByID( prefInfo.window, &kAlwaysBtn, &prefInfo.alwaysBtn ); assert(prefInfo.alwaysBtn);
763 GetControlByID( prefInfo.window, &kOpenALBtn, &prefInfo.openALBtn ); assert(prefInfo.openALBtn);
767 // Disable the "choose monitor" button if we've only got one to pick from
769 prefInfo.multiMonitor = CanUserPickMonitor();
771 if (!prefInfo.multiMonitor)
773 DisableControl (prefInfo.chooseMonitorsBtn);
774 prefInfo.prefDisplayID = 0;
777 // Prepare the resolutions and refresh rates popup menus
778 AdjustDisplayControls(&prefInfo);
780 // Set up the controls
782 SetControlValue (prefInfo.refreshRatePopup, prefInfo.freqMenuIndex);
783 SetControlValue (prefInfo.fullscreenBtn, prefInfo.prefGameDisplayMode == kFullScreen);
784 SetControlValue (prefInfo.inAWindowBtn, prefInfo.prefGameDisplayMode == kWindow);
785 SetControlValue (prefInfo.alwaysBtn, prefInfo.prefAlways);
786 SetControlValue (prefInfo.openALBtn, prefInfo.prefOpenAL);
789 // Create our UPP and install the handler.
791 EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess };
792 EventHandlerUPP handler = GetPrefHandlerUPP();
793 InstallWindowEventHandler( prefInfo.window, handler, 1, &cmdEvent, &prefInfo, NULL );
795 // Position and show the window
796 RepositionWindow( prefInfo.window, NULL, kWindowAlertPositionOnMainScreen );
799 *outWindow = prefInfo.window;
805 //------------------------------------------------------------------------------------
806 // ¥ RunGameDisplayPreferencesDialog
807 //------------------------------------------------------------------------------------
808 // Runs the Mac-specific preferences dialog.
810 OSStatus RunGameDisplayPreferencesDialog(GameDisplayInfo *outGDInfo, WindowRef inWindow)
812 PrefInfo *prefInfo = (PrefInfo*)GetWRefCon(inWindow);
814 ShowWindow( inWindow );
816 // Now we run modally. We will remain here until the PrefHandler
817 // calls QuitAppModalLoopForWindow if the user clicks OK or
820 RunAppModalLoopForWindow( inWindow );
822 // OK, we're done. Dispose of our window.
823 // TODO: Are we supposed to uninstall event handlers?
824 DisposeWindow( inWindow );
826 // Return settings to caller
828 if (prefInfo->okPressed)
830 outGDInfo->mode = prefInfo->prefGameDisplayMode;
831 outGDInfo->width = prefInfo->prefWidth;
832 outGDInfo->height = prefInfo->prefHeight;
833 outGDInfo->depth = prefInfo->prefDepth;
834 outGDInfo->frequency = prefInfo->prefFrequency;
835 outGDInfo->resFlags = prefInfo->prefResFlags;
836 outGDInfo->displayID = prefInfo->prefDisplayID;
839 return prefInfo->okPressed ? noErr : userCanceledErr;