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