]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/osx/PreferencesDialog.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / sys / osx / PreferencesDialog.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28
29 // !!! FIXME:
30 void Sys_DoPreferences( void ) {}
31 #if 0
32 #include "../../idlib/precompiled.h"
33 #include <Carbon/Carbon.h>
34 #include "PreferencesDialog.h"
35 #include "PickMonitor.h"
36 #include <list>
37 #include <set>
38
39 static idCVar r_stretched( "r_stretched", "0", CVAR_ARCHIVE | CVAR_BOOL, "Used stretched resolution" );
40
41 #define kPref_PrefsDialogAlways CFSTR("PrefsDialogAlways")
42 #define kPref_PrefsDialogOpenAL CFSTR("UseOpenAL")
43
44 #ifndef kAppCreator
45 #define kAppCreator                             'DOM3'  // Creator type
46 #endif
47
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)
50
51 // Data to be presented and edited in the prefs dialog
52 struct PrefInfo
53 {
54         // prefs values
55         GameDisplayMode                                 prefGameDisplayMode;
56         CGDirectDisplayID                               prefDisplayID;
57         int                                                             prefWidth;
58         int                                                             prefHeight;
59         int                                                             prefDepth;
60         Fixed                                                   prefFrequency;
61         UInt32                                                  prefResFlags;
62         Boolean                                                 prefAlways;
63         Boolean                                                 prefOpenAL;
64         
65         bool                                                    okPressed;              // Set to true if the user pressed the OK button
66         
67         // The following are private data passed from GameDisplayPreferencesDialog() to it's command handler.
68         WindowRef                                               window;
69         ControlRef                                              fullscreenBtn;
70         ControlRef                                              inAWindowBtn;
71         ControlRef                                              resolutionPopup;
72         ControlRef                                              refreshRatePopup;
73         ControlRef                                              chooseMonitorsBtn;
74         ControlRef                                              alwaysBtn;
75         ControlRef                                              openALBtn;
76         
77         ValidModeCallbackProc                   callback;               // To validate display modes
78         
79         bool                                                    multiMonitor;   // Does user have multiple monitors
80         std::list<Fixed>                                refreshRates;   // List of refresh rates available for the selected monitor
81         SInt32                                                  freqMenuIndex;
82 };
83
84
85 #pragma mark -
86
87 bool R_GetModeInfo( int *width, int *height, int mode );
88
89 static int GetScreenIndexForDisplayID( CGDirectDisplayID inDisplayID ) {
90         unsigned int i;
91         OSErr err;
92         int r_screen = -1;
93         CGDisplayCount count;
94
95         err = CGGetActiveDisplayList(0, NULL, &count);
96         if (noErr == err) {
97                 CGDirectDisplayID displays[count];
98                 err = CGGetActiveDisplayList(count, displays, &count);
99                 if (noErr == err) {
100                         for ( i = 0; i < count; i++)
101                                 if (displays[i] == inDisplayID)
102                                         r_screen = i;
103                 }
104         }
105         return r_screen;
106 }
107
108 static CGDirectDisplayID GetDisplayIDForScreenIndex( int inScreenIndex ) {
109         OSErr err;
110         int r_screen = -1;
111         CGDisplayCount count;
112         
113         err = CGGetActiveDisplayList(0, NULL, &count);
114         if (noErr == err) {
115                 CGDirectDisplayID displays[count];
116                 err = CGGetActiveDisplayList(count, displays, &count);
117                 if (noErr == err) {
118                         if ( inScreenIndex >= 0 && inScreenIndex <= count )
119                                 return displays[inScreenIndex];
120                 }
121         }
122         return (CGDirectDisplayID)r_screen;
123 }
124
125     
126
127 void Sys_DoPreferences( void ) {
128            
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;
137         GetKeys(*keymap);
138         
139         Boolean prefAways, keyFound, useOpenAL;
140         prefAways = CFPreferencesGetAppBooleanValue ( kPref_PrefsDialogAlways, kCFPreferencesCurrentApplication, &keyFound );
141         bool fAlways = prefAways && keyFound;
142                 
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" ) );
147                 
148                 int w = 800, h = 600;
149                 R_GetModeInfo( &w, &h, cvarSystem->GetCVarInteger( "r_mode" ) );
150                 info.width = w;
151                 info.height = h;
152                 info.depth = 32;
153                 info.frequency = cvarSystem->GetCVarInteger( "r_maxDisplayRefresh" );
154                 info.windowLoc.x = 0;
155                 info.windowLoc.y = 0;
156                 info.flags = 0;
157                 info.resFlags = 0;
158                 if ( r_stretched.GetBool() )
159                         info.resFlags |= kRes_Stretched;
160                         
161                 WindowRef prefWindow;
162                 if ( CreateGameDisplayPreferencesDialog( &info, &prefWindow ) == noErr ) {
163                         if ( RunGameDisplayPreferencesDialog( &info, prefWindow ) == noErr ) {
164                                 cvarSystem->SetCVarBool( "r_fullscreen",  info.mode == kFullScreen );
165
166                                 int i = 0;
167                                 int r_mode = -1;
168                                 while ( r_mode == -1 && R_GetModeInfo( &w, &h, i ) ) {
169                                         if ( w == info.width && h == info.height )
170                                                 r_mode = i;
171                                         i++;
172                                 }
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 );
177                                 }
178
179                                 float r = (float) info.width / (float) info.height;
180                                 if ( r > 1.7f )
181                                         cvarSystem->SetCVarInteger( "r_aspectRatio", 1 );       // 16:9
182                                 else if ( r > 1.55f )
183                                         cvarSystem->SetCVarInteger( "r_aspectRatio", 2 );       // 16:10
184                                 else
185                                         cvarSystem->SetCVarInteger( "r_aspectRatio", 0 );       // 4:3
186                                 
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 ) );
191                         }
192                         else {
193                                 Sys_Quit();             
194                         }
195                 }
196         }
197         useOpenAL = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogOpenAL, kCFPreferencesCurrentApplication, &keyFound);
198         if ( keyFound && useOpenAL ) {
199                 cvarSystem->SetCVarInteger( "com_asyncSound", 1 );
200                 cvarSystem->SetCVarInteger( "s_useOpenAL", 1 );
201         }
202         else {
203                 cvarSystem->SetCVarInteger( "com_asyncSound", 2 );
204                 cvarSystem->SetCVarInteger( "s_useOpenAL", 0 );
205         }
206 }
207
208
209 #pragma mark -
210
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)
214
215 // Command IDs used in the NIB file
216 enum
217 {
218         kCmdFullscreen                          = 'Full',
219         kCmdInAWindow                           = 'Wind',
220         kCmdResolution                          = 'Reso',
221         kCmdRefreshRate                         = 'Refr',
222         kCmdChooseMonitors                      = 'Moni',
223 };
224
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 };
233
234 struct Res
235 {
236         int width;
237         int height;
238         int depth;
239         UInt32 resFlags;
240 };
241
242 static bool operator< (const Res& a, const Res& b)
243 {
244         if (a.width == b.width)
245         {
246                 if (a.height == b.height)
247                 {
248                         if (a.resFlags == b.resFlags)
249                         {
250                                 return (a.depth < b.depth);
251                         }
252                         return (a.resFlags < b.resFlags);
253                 }
254                 return (a.height < b.height);
255         }
256         return (a.width < b.width);
257 }
258
259 inline Res MakeRes(int width, int height, int depth)
260 {
261         Res temp = { width, height, depth, 0 };
262         return temp;
263 }
264
265 inline Res MakeRes(int width, int height, int depth, UInt32 resFlags)
266 {
267         Res temp = { width, height, depth, resFlags };
268         return temp;
269 }
270
271 static bool ValidDisplayID (CGDirectDisplayID inDisplayID)
272 {
273         unsigned int i;
274         CGDisplayErr err;
275         CGDisplayCount count;
276         
277         err = CGGetActiveDisplayList(0, NULL, &count);
278         if (noErr == err)
279         {
280                 CGDirectDisplayID displays[count];
281                 err = CGGetActiveDisplayList(count, displays, &count);
282                 if (noErr == err)
283                 {
284                         for ( i = 0; i < count; i++)
285                                 if (displays[i] == inDisplayID)
286                                         return true;
287                 }
288         }
289         return false;
290 }
291
292 static int BuildResolutionList(CGDirectDisplayID inDisplayID, Res *ioList, ValidModeCallbackProc inCallback)
293 {
294         std::set<Res> modes;
295         int i, total = 0;
296         
297         if (inDisplayID == (CGDirectDisplayID)-1)       // special case, not associated with any display
298         {
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++)
303                 {
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) );
306                 }
307         }
308         else
309         {       
310                 CGDirectDisplayID displayID = inDisplayID ? inDisplayID : kCGDirectMainDisplay;
311                 CFArrayRef modeArrayRef = CGDisplayAvailableModes(displayID);
312                 CFIndex numModes = CFArrayGetCount(modeArrayRef);
313                 
314                 for (i = 0; i < numModes; i++)
315                 {
316                         CFDictionaryRef modeRef = (CFDictionaryRef)CFArrayGetValueAtIndex(modeArrayRef, i);
317                         
318                         long value = 0;
319                         CFNumberRef valueRef;
320                         Boolean success;
321                         
322                         valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayBitsPerPixel);
323                         success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
324                         int depth = value;
325                         if (depth != 32) continue;
326                         
327                         valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayWidth);
328                         success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
329                         int width = value;
330                         
331                         valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayHeight);
332                         success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
333                         int height = value;
334                         
335                         UInt32 resFlags = 0;
336                         CFBooleanRef boolRef;
337                         if (CFDictionaryGetValueIfPresent (modeRef, kCGDisplayModeIsStretched, (const void **)&boolRef))
338                                 if (CFBooleanGetValue (boolRef))
339                                         resFlags |= kRes_Stretched;
340                         
341                         
342                         if (inCallback)
343                                 success = inCallback(displayID, width, height, depth, 0);
344                         else
345                                 success = true;
346                         
347                         if (success)
348                                 modes.insert(MakeRes(width, height, depth, resFlags));
349                 }
350         }
351         
352         total = modes.size();
353         
354         if (ioList)
355         {
356                 std::set<Res>::iterator it = modes.begin();
357                 for (i = 0; it != modes.end(); i++)
358                         ioList[i] = *it++;
359         }
360         
361         return total;   
362 }
363
364
365
366
367 static void BuildRefreshRates(CGDirectDisplayID inDisplayID, int inWidth, int inHeight, std::list<Fixed>* inList, ValidModeCallbackProc inCallback)
368 {
369         CGDirectDisplayID displayID = inDisplayID ? inDisplayID : kCGDirectMainDisplay;
370         
371         CFArrayRef modeArrayRef = CGDisplayAvailableModes(displayID);
372         CFIndex numModes = CFArrayGetCount(modeArrayRef);
373
374         inList->clear();
375         
376         for (int i = 0; i < numModes; i++)
377         {
378                 CFDictionaryRef modeRef = (CFDictionaryRef)CFArrayGetValueAtIndex(modeArrayRef, i);
379                 
380                 long value = 0;
381                 CFNumberRef valueRef;
382                 Boolean success;
383                 
384                 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayBitsPerPixel);
385                 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
386                 int depth = value;
387                 if (depth != 32) continue;
388                 
389                 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayWidth);
390                 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
391                 int width = value;
392                 
393                 valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayHeight);
394                 success = CFNumberGetValue(valueRef, kCFNumberLongType, &value);
395                 int height = value;
396                 
397                 if (width == inWidth && height == inHeight)
398                 {
399                         double freqDouble;
400                         valueRef = (CFNumberRef)CFDictionaryGetValue(modeRef, kCGDisplayRefreshRate);
401                         success = CFNumberGetValue(valueRef, kCFNumberDoubleType, &freqDouble);
402                         Fixed   freq = FloatToFixed(freqDouble);
403                         if (inCallback)
404                                 success = inCallback(displayID, width, height, depth, freq);
405                         else
406                                 success = true;
407                         if (success)
408                                 inList->push_back(freq);
409                 }
410         }
411
412         // Disallow 0, which we reserve to mean "automatic"
413         inList->remove(0);
414         
415         inList->sort();
416         
417         // Remove duplicates - yes they can occur.
418         inList->unique();
419 }
420
421 static void BuildRefreshPopupButton(ControlRef inControl, std::list<Fixed>* inList)
422 {
423         MenuRef menu = GetControlPopupMenuRef(inControl);
424         assert(menu);
425         if (!menu) return;
426
427         // The menu has two permanent items - "Auto" & a divider line. Delete everything else.
428         DeleteMenuItems(menu, 3, CountMenuItems(menu)-2);
429         
430         for (std::list<Fixed>::const_iterator iter = inList->begin(); iter != inList->end(); ++iter)
431         {
432                 float value = FixedToFloat(*iter);
433                 CFStringRef menuString = CFStringCreateWithFormat (kCFAllocatorDefault, 0, CFSTR("%g Hz"), value);
434                 InsertMenuItemTextWithCFString(menu, menuString, CountMenuItems(menu), 0, 0);
435         }
436         
437         SetControlMaximum(inControl, CountMenuItems(menu));
438 }
439
440 static SInt32 FindRefreshPopupMenuItem(std::list<Fixed>* inList, Fixed inFrequency)
441 {
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)
444         {
445                 if (*iter == inFrequency)
446                         return index;
447                 index++;
448         }
449         return 1;       // Return the "Automatic" item if we didn't find a match
450 }
451
452 static void BuildResolutionPopupButton(ControlRef inControl, CGDirectDisplayID inDisplayID, ValidModeCallbackProc inCallback)
453 {
454         // Get the list of valid resolutions
455         int count = BuildResolutionList(inDisplayID, NULL, inCallback);
456         Res resList[count];
457         BuildResolutionList(inDisplayID, resList, inCallback);
458         
459         // Clear the menu
460         MenuRef menu = GetControlPopupMenuRef(inControl);
461         assert(menu);
462         if (!menu) return;
463         DeleteMenuItems(menu, 1, CountMenuItems(menu));
464
465         OSStatus err;
466
467         while (count--)
468         {
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]);
473         }
474         
475         SetControlMaximum(inControl, CountMenuItems(menu));
476 }
477
478 static void GetResolutionFromPopupMenuItem(ControlRef inControl, MenuItemIndex inItem, int *outX, int *outY, int *outDepth, UInt32 *outResFlags)
479 {
480         MenuRef menu = GetControlPopupMenuRef(inControl);
481         Res res;
482         OSStatus err;
483         
484         err = GetMenuItemProperty (menu, inItem, kAppCreator, 'Res ', sizeof(res), NULL, &res);
485         if (!err)
486         {
487                 *outX = res.width;
488                 *outY = res.height;
489                 *outResFlags = res.resFlags;
490                 *outDepth = 32;
491         }
492 }
493
494 static void AdjustResolutionPopupMenu(ControlRef inControl, CGDirectDisplayID inDisplayID, bool isFullscreen, int& screenwidth, int& screenheight, int& screendepth, UInt32& screenResFlags)
495 {
496         int screenX = INT_MAX, screenY = INT_MAX;
497
498         // In windowed mode, you have to disable resolutions that are larger than the current screen size
499         if (!isFullscreen)
500         {
501                 screenX = (int)CGDisplayPixelsWide(inDisplayID);
502                 screenY = (int)CGDisplayPixelsHigh(inDisplayID);
503         }
504         
505         MenuRef menu = GetControlPopupMenuRef(inControl);
506         int resX, resY, depth;
507         UInt32 resFlags;
508         int count = CountMenuItems(menu);
509         int item;
510                 
511         for( item = 1; item <= count; item++)
512         {
513                 GetResolutionFromPopupMenuItem(inControl, item, &resX, &resY, &depth, &resFlags);
514
515                 if (screenX < resX || screenY < resY)
516                         DisablePopupMenuItem(inControl, item);
517                 else
518                         EnablePopupMenuItem(inControl, item);
519                         
520                 if (resX == screenwidth && resY == screenheight && depth == screendepth && resFlags == screenResFlags)
521                         SetControlValue(inControl, item);
522         }
523         
524         // If we just disabled the current item, then choose something else.
525         if (!IsPopupMenuItemEnabled(inControl, GetControlValue (inControl)))
526         {
527                 for(item = 1; item <= count; item++)
528                 {
529                         if (IsPopupMenuItemEnabled(inControl, item))
530                         {
531                                 SetControlValue(inControl, item);
532                                 GetResolutionFromPopupMenuItem(inControl, item, &screenwidth, &screenheight, &screendepth, &screenResFlags);
533                                 break;
534                         }
535                 }
536         }
537 }
538
539 static void AdjustDisplayControls(PrefInfo *prefInfo)
540 {
541         // Build new resolution popup and select appropriate resolution
542         if ((prefInfo->prefGameDisplayMode != kFullScreen))
543         {
544                 BuildResolutionPopupButton(prefInfo->resolutionPopup, (CGDirectDisplayID)-1, prefInfo->callback);
545                 if (prefInfo->multiMonitor)
546                         EnableControl(prefInfo->chooseMonitorsBtn);
547         }
548         else
549         {
550                 BuildResolutionPopupButton(prefInfo->resolutionPopup, prefInfo->prefDisplayID, prefInfo->callback);
551                 if (prefInfo->multiMonitor)
552                         EnableControl(prefInfo->chooseMonitorsBtn);
553         }
554         AdjustResolutionPopupMenu(prefInfo->resolutionPopup, prefInfo->prefDisplayID,
555                 prefInfo->prefGameDisplayMode == kFullScreen, 
556                 prefInfo->prefWidth, prefInfo->prefHeight, prefInfo->prefDepth, prefInfo->prefResFlags);
557         
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);
562
563         if (prefInfo->refreshRates.size() == 0)
564         {       // No refresh rates, so pick Auto
565                 prefInfo->freqMenuIndex = 1;
566                 prefInfo->prefFrequency = 0;
567         }
568         else
569         {
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
573         }
574         SetControlValue (prefInfo->refreshRatePopup, prefInfo->freqMenuIndex);
575
576         // Disable refresh rate if NOT fullscreen
577         if ((prefInfo->prefGameDisplayMode != kFullScreen) || (prefInfo->refreshRates.size() == 0))
578                 DisableControl (prefInfo->refreshRatePopup);
579         else
580                 EnableControl (prefInfo->refreshRatePopup);
581 }
582
583 #pragma mark -
584
585 static pascal OSStatus PrefHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* inUserData )
586 {
587         #pragma unused( inHandler )
588         
589         HICommand                       cmd;
590         OSStatus                        result = eventNotHandledErr;
591         PrefInfo*                       prefInfo = (PrefInfo*)inUserData;
592
593         // The direct object for a 'process commmand' event is the HICommand.
594         // Extract it here and switch off the command ID.
595
596         GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd );
597
598         switch ( cmd.commandID )
599         {
600                 case kHICommandOK:
601                         
602                         prefInfo->okPressed = true;
603                         
604                         prefInfo->prefAlways = GetControlValue (prefInfo->alwaysBtn);
605                         prefInfo->prefOpenAL = GetControlValue (prefInfo->openALBtn);
606                         
607                         CFPreferencesSetAppValue (kPref_PrefsDialogAlways, 
608                                                                           prefInfo->prefAlways ? kCFBooleanTrue : kCFBooleanFalse,
609                                                                           kCFPreferencesCurrentApplication);
610
611                         CFPreferencesSetAppValue (kPref_PrefsDialogOpenAL, 
612                                                                           prefInfo->prefOpenAL ? kCFBooleanTrue : kCFBooleanFalse,
613                                                                           kCFPreferencesCurrentApplication);
614                         
615                         CFPreferencesAppSynchronize (kCFPreferencesCurrentApplication);
616
617                         QuitAppModalLoopForWindow( prefInfo->window );
618                         result = noErr;
619                         break;
620                 
621                 case kHICommandCancel:
622                         
623                         prefInfo->okPressed = false;
624                         
625                         QuitAppModalLoopForWindow( prefInfo->window );
626                         result = noErr;
627                         break;
628                 
629                 case kCmdFullscreen:
630                 case kCmdInAWindow:
631                         if (cmd.commandID == kCmdFullscreen)
632                                 prefInfo->prefGameDisplayMode = kFullScreen;
633                         else
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);
639                         else 
640                                 DisableControl (prefInfo->refreshRatePopup);
641                         if (prefInfo->multiMonitor)
642                                 EnableControl (prefInfo->chooseMonitorsBtn);
643                         else
644                                 DisableControl (prefInfo->chooseMonitorsBtn);
645                                 
646                         // Adjust resolutions, refresh rates
647                         AdjustDisplayControls(prefInfo);
648                         result = noErr;
649                         break;
650                 
651
652                 case kCmdChooseMonitors:
653                 {
654                         PickMonitor((DisplayIDType*)&prefInfo->prefDisplayID, prefInfo->window);
655                         // Adjust resolutions, refresh rates for potentially new display ID
656                         AdjustDisplayControls(prefInfo);
657                         break;
658                 }
659
660                 case kCmdResolution:
661                 {
662                         // Pick a new resolution
663                         int item = GetControlValue(prefInfo->resolutionPopup);
664                         GetResolutionFromPopupMenuItem(prefInfo->resolutionPopup, item, &prefInfo->prefWidth, &prefInfo->prefHeight, &prefInfo->prefDepth, &prefInfo->prefResFlags);
665         
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);
673
674                         // Disable refresh rate if NOT fullscreen
675                         if ((prefInfo->prefGameDisplayMode != kFullScreen) || (prefInfo->refreshRates.size() == 0))
676                                 DisableControl (prefInfo->refreshRatePopup);
677                         else
678                                 EnableControl (prefInfo->refreshRatePopup);
679                         
680                         break;
681                 }
682
683                 case kCmdRefreshRate:
684                 {
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;
689                         else
690                         {
691                                 std::list<Fixed>::const_iterator iter = prefInfo->refreshRates.begin();
692                                 for (int i = 0; i < prefInfo->freqMenuIndex-3; i++)
693                                         iter++;
694                                 prefInfo->prefFrequency = *iter;
695                         }
696                         break;
697                 }
698
699
700         }       
701         return result;
702 }
703
704 #pragma mark -
705
706 static DEFINE_ONE_SHOT_HANDLER_GETTER(PrefHandler)
707
708 OSStatus CreateGameDisplayPreferencesDialog(const GameDisplayInfo *inGDInfo,
709                                                                                         WindowRef *outWindow, ValidModeCallbackProc inCallback)
710 {
711         OSStatus err = noErr;
712         
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.
717         
718         static PrefInfo prefInfo;
719         
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;
729         
730         Boolean result;
731         Boolean keyFound;
732         result = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogAlways, kCFPreferencesCurrentApplication, &keyFound);
733         prefInfo.prefAlways = result && keyFound;
734         result = CFPreferencesGetAppBooleanValue (kPref_PrefsDialogOpenAL, kCFPreferencesCurrentApplication, &keyFound);
735         prefInfo.prefOpenAL = result && keyFound;
736         
737         prefInfo.callback = inCallback;
738         
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.
741         
742         if (!ValidDisplayID(prefInfo.prefDisplayID))
743                 prefInfo.prefDisplayID = kCGDirectMainDisplay;  // revert to main
744         
745         // Fetch the dialog
746         
747         IBNibRef aslNib;
748         CFBundleRef theBundle = CFBundleGetMainBundle();
749         err = CreateNibReferenceWithCFBundle(theBundle, CFSTR("ASLCore"), &aslNib);
750         err = ::CreateWindowFromNib(aslNib, CFSTR( "Preferences" ), &prefInfo.window );
751         if (err != noErr)
752                 return err;
753         SetWRefCon(prefInfo.window, (long)&prefInfo);
754         
755         // Locate all the controls
756         
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);
764         
765         
766         
767         // Disable the "choose monitor" button if we've only got one to pick from
768         
769         prefInfo.multiMonitor = CanUserPickMonitor();
770         
771         if (!prefInfo.multiMonitor)
772         {
773                 DisableControl (prefInfo.chooseMonitorsBtn);
774                 prefInfo.prefDisplayID = 0;
775         }
776         
777         // Prepare the resolutions and refresh rates popup menus
778         AdjustDisplayControls(&prefInfo);
779         
780         // Set up the controls
781         
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);
787
788         
789         // Create our UPP and install the handler.
790         
791         EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess };
792         EventHandlerUPP handler = GetPrefHandlerUPP();
793         InstallWindowEventHandler( prefInfo.window, handler, 1, &cmdEvent, &prefInfo, NULL );
794         
795         // Position and show the window
796         RepositionWindow( prefInfo.window, NULL, kWindowAlertPositionOnMainScreen );
797         
798         if (outWindow)
799                 *outWindow = prefInfo.window;
800         
801         return err;
802 }
803
804
805 //------------------------------------------------------------------------------------
806 // ¥ RunGameDisplayPreferencesDialog
807 //------------------------------------------------------------------------------------
808 // Runs the Mac-specific preferences dialog.
809
810 OSStatus RunGameDisplayPreferencesDialog(GameDisplayInfo *outGDInfo, WindowRef inWindow)
811 {
812         PrefInfo *prefInfo = (PrefInfo*)GetWRefCon(inWindow);
813         
814         ShowWindow( inWindow );
815         
816         // Now we run modally. We will remain here until the PrefHandler
817         // calls QuitAppModalLoopForWindow if the user clicks OK or
818         // Cancel.
819         
820         RunAppModalLoopForWindow( inWindow );
821         
822         // OK, we're done. Dispose of our window.
823         // TODO: Are we supposed to uninstall event handlers?
824         DisposeWindow( inWindow );
825         
826         // Return settings to caller
827         
828         if (prefInfo->okPressed)
829         {               
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;
837         }
838         
839         return prefInfo->okPressed ? noErr : userCanceledErr;
840 }
841
842 #endif
843