]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/osx/PickMonitor.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / sys / osx / PickMonitor.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 #include "../../idlib/precompiled.h"
30 #include <Carbon/Carbon.h>
31 #include "PickMonitor.h"
32
33 //====================================================================================
34 //      CONSTANTS
35 //====================================================================================
36
37 #define kMaxMonitors            16
38
39 //====================================================================================
40 //      TYPES
41 //====================================================================================
42
43 typedef struct
44 {
45         GDHandle        device;
46         Rect            origRect;
47         Rect            scaledRect;
48         int                     isMain;
49 }
50 Monitor;
51
52
53 //====================================================================================
54 //      GLOBALS
55 //====================================================================================
56 static GDHandle sSelectedDevice;
57 static int sNumMonitors;
58 static Monitor sMonitors[kMaxMonitors];
59
60 static RGBColor rgbBlack = { 0x0000, 0x0000, 0x0000 };
61 static RGBColor rgbWhite = { 0xffff, 0xffff, 0xffff };
62 static RGBColor rgbGray  = { 0x5252, 0x8A8A, 0xCCCC };  // this is the blue used in the Displays control panel
63
64 //====================================================================================
65 //      MACROS
66 //====================================================================================
67
68 #undef PtInRect
69 #undef OffsetRect
70 #undef InsetRect
71 #undef EraseRect
72 #undef MoveTo
73 #undef LineTo
74
75
76 //====================================================================================
77 //      IMPLEMENTATION
78 //====================================================================================
79
80 //-----------------------------------------------------------------------------
81 //      SetupUserPaneProcs
82 //-----------------------------------------------------------------------------
83 //      Call this to initialize the specified user pane control before displaying 
84 //      the dialog window. Pass NULL for any user pane procs you don't need to install.
85
86 OSErr SetupUserPaneProcs(       ControlRef inUserPane,
87                                                         ControlUserPaneDrawProcPtr inDrawProc, 
88                                                         ControlUserPaneHitTestProcPtr inHitTestProc,
89                                                         ControlUserPaneTrackingProcPtr inTrackingProc)
90 {
91         OSErr   err = noErr;
92         ControlUserPaneDrawUPP drawUPP;
93         ControlUserPaneHitTestUPP hitTestUPP;
94         ControlUserPaneTrackingUPP trackingUPP;
95         
96         if (0 == inUserPane) return paramErr;
97         
98         if (inDrawProc && noErr == err)
99         {
100                 drawUPP = NewControlUserPaneDrawUPP(inDrawProc);
101
102                 if (0 == drawUPP)
103                         err = memFullErr;
104                 else
105                         err = SetControlData(   inUserPane,
106                                                                         kControlEntireControl,
107                                                                         kControlUserPaneDrawProcTag,
108                                                                         sizeof(ControlUserPaneDrawUPP),
109                                                                         (Ptr)&drawUPP);
110         }
111         if (inHitTestProc && noErr == err)
112         {
113                 hitTestUPP = NewControlUserPaneHitTestUPP(inHitTestProc);
114
115                 if (0 == hitTestUPP)
116                         err = memFullErr;
117                 else
118                         err = SetControlData(   inUserPane,
119                                                                         kControlEntireControl, 
120                                                                         kControlUserPaneHitTestProcTag,
121                                                                         sizeof(ControlUserPaneHitTestUPP),
122                                                                         (Ptr)&hitTestUPP);
123         }
124         if (inTrackingProc && noErr == err)
125         {
126                 trackingUPP = NewControlUserPaneTrackingUPP(inTrackingProc);
127                 
128                 if (0 == trackingUPP)
129                         err = memFullErr;
130                 else
131                         err = SetControlData(   inUserPane,
132                                                                         kControlEntireControl, 
133                                                                         kControlUserPaneTrackingProcTag,
134                                                                         sizeof(ControlUserPaneTrackingUPP),
135                                                                         (Ptr)&trackingUPP);
136         }
137         
138         return err;
139 }
140
141
142 //-----------------------------------------------------------------------------
143 //      DisposeUserPaneProcs
144 //-----------------------------------------------------------------------------
145 //      Call this to clean up when you're done with the specified user pane control.
146
147 OSErr DisposeUserPaneProcs(ControlRef inUserPane)
148 {       
149         ControlUserPaneDrawUPP drawUPP;
150         ControlUserPaneHitTestUPP hitTestUPP;
151         ControlUserPaneTrackingUPP trackingUPP;
152         Size actualSize;
153         OSErr err;
154         
155         err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneDrawProcTag, sizeof(ControlUserPaneDrawUPP), (Ptr)&drawUPP, &actualSize);
156         if (err == noErr) DisposeControlUserPaneDrawUPP(drawUPP);
157
158         err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneHitTestProcTag, sizeof(ControlUserPaneHitTestUPP), (Ptr)&hitTestUPP, &actualSize);
159         if (err == noErr) DisposeControlUserPaneHitTestUPP(hitTestUPP);
160
161         err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneTrackingProcTag, sizeof(ControlUserPaneTrackingUPP), (Ptr)&trackingUPP, &actualSize);
162         if (err == noErr) DisposeControlUserPaneTrackingUPP(trackingUPP);
163
164         return noErr;
165 }
166
167 #pragma mark -
168
169 //-----------------------------------------------------------------------------
170 //      drawProc
171 //-----------------------------------------------------------------------------
172 //      Custom drawProc for our UserPane control.
173
174 static pascal void drawProc(ControlRef inControl, SInt16 inPart)
175 {
176         #pragma unused(inControl, inPart)
177         
178         int i;
179         RGBColor saveForeColor;
180         RGBColor saveBackColor;
181         PenState savePenState;
182
183         GetForeColor(&saveForeColor);   
184         GetBackColor(&saveBackColor);   
185         GetPenState(&savePenState);
186
187         RGBForeColor(&rgbBlack);
188         RGBBackColor(&rgbWhite);
189         PenNormal();
190         
191         for (i = 0; i < sNumMonitors; i++)
192         {
193                 RGBForeColor(&rgbGray);
194                 PaintRect(&sMonitors[i].scaledRect);
195                 if (sMonitors[i].isMain)
196                 {
197                         Rect r = sMonitors[i].scaledRect;
198                         InsetRect(&r, 1, 1);
199                         r.bottom = r.top + 6;
200                         RGBForeColor(&rgbWhite);
201                         PaintRect(&r);
202                         RGBForeColor(&rgbBlack);
203                         PenSize(1,1);
204                         MoveTo(r.left, r.bottom);
205                         LineTo(r.right, r.bottom);
206                 }
207                 if (sMonitors[i].device == sSelectedDevice)
208                 {
209                         PenSize(3,3);
210                         RGBForeColor(&rgbBlack);
211                         FrameRect(&sMonitors[i].scaledRect);
212                 }
213                 else
214                 {
215                         PenSize(1,1);
216                         RGBForeColor(&rgbBlack);
217                         FrameRect(&sMonitors[i].scaledRect);
218                 }
219         }
220         
221         // restore the original pen state and colors
222         RGBForeColor(&saveForeColor);   
223         RGBBackColor(&saveBackColor);   
224         SetPenState(&savePenState);
225 }
226
227
228 //-----------------------------------------------------------------------------
229 //      hitTestProc
230 //-----------------------------------------------------------------------------
231 //      Custom hitTestProc for our UserPane control.
232 //      This allows FindControlUnderMouse() to locate our control, which allows
233 //      ModalDialog() to call TrackControl() or HandleControlClick() for our control.
234
235 static pascal ControlPartCode hitTestProc(ControlRef inControl, Point inWhere)
236 {
237         // return a valid part code so HandleControlClick() will be called
238         return kControlButtonPart;
239 }
240
241
242 //-----------------------------------------------------------------------------
243 //      trackingProc
244 //-----------------------------------------------------------------------------
245 //      Custom trackingProc for our UserPane control.
246 //      This won't be called for our control unless the kControlHandlesTracking feature
247 //      bit is specified when the userPane is created.
248
249 static pascal ControlPartCode trackingProc (
250                                         ControlRef inControl,
251                                         Point inStartPt,
252                                         ControlActionUPP inActionProc)
253 {
254         #pragma unused (inControl, inStartPt, inActionProc)
255         int i;
256
257         for (i = 0; i < sNumMonitors; i++)
258         {
259                 if (PtInRect(inStartPt, &sMonitors[i].scaledRect))
260                 {
261                         if (sMonitors[i].device != sSelectedDevice)
262                         {
263                                 sSelectedDevice = sMonitors[i].device;
264                                 DrawOneControl(inControl);
265                         }
266                         break;
267                 }
268         }
269         
270         return kControlNoPart;
271 }
272
273
274 #pragma mark -
275
276
277 //-----------------------------------------------------------------------------
278 //      SetupPickMonitorPane
279 //-----------------------------------------------------------------------------
280 //      Call this to initialize the user pane control that is the Pick Monitor
281 //      control. Pass the ControlRef of the user pane control and a display ID
282 //      for the monitor you want selected by default (pass 0 for the main monitor).
283 //      Call this function before displaying the dialog window.
284
285 OSErr SetupPickMonitorPane(ControlRef inPane, DisplayIDType inDefaultMonitor)
286 {
287         GDHandle dev = GetDeviceList();
288         OSErr err = noErr;
289         
290         // make the default monitor the selected device
291         if (inDefaultMonitor)
292                 DMGetGDeviceByDisplayID(inDefaultMonitor, &sSelectedDevice, true);
293         else
294                 sSelectedDevice = GetMainDevice();
295
296         // build the list of monitors
297         sNumMonitors = 0;
298         while (dev && sNumMonitors < kMaxMonitors)
299         {
300                 if (TestDeviceAttribute(dev, screenDevice) && TestDeviceAttribute(dev, screenActive))
301                 {
302                         sMonitors[sNumMonitors].device = dev;
303                         sMonitors[sNumMonitors].origRect = (**dev).gdRect;
304                         sMonitors[sNumMonitors].isMain = (dev == GetMainDevice());
305                         sNumMonitors++;
306                 }
307                 dev = GetNextDevice(dev);
308         }
309
310         // calculate scaled rects
311         if (sNumMonitors)
312         {
313                 Rect origPaneRect, paneRect;
314                 Rect origGrayRect, grayRect, scaledGrayRect;
315                 float srcAspect, dstAspect, scale;
316                 int i;
317                 
318                 GetControlBounds(inPane, &origPaneRect);
319                 paneRect = origPaneRect;
320                 OffsetRect(&paneRect, -paneRect.left, -paneRect.top);
321                 
322                 GetRegionBounds(GetGrayRgn(), &origGrayRect);
323                 grayRect = origGrayRect;
324                 OffsetRect(&grayRect, -grayRect.left, -grayRect.top);
325                 
326                 srcAspect = (float)grayRect.right / (float)grayRect.bottom;
327                 dstAspect = (float)paneRect.right / (float)paneRect.bottom;
328                 
329                 scaledGrayRect = paneRect;
330                 
331                 if (srcAspect < dstAspect)
332                 {
333                         scaledGrayRect.right = (float)paneRect.bottom * srcAspect;
334                         scale = (float)scaledGrayRect.right / grayRect.right;
335                 }
336                 else
337                 {
338                         scaledGrayRect.bottom = (float)paneRect.right / srcAspect;
339                         scale = (float)scaledGrayRect.bottom / grayRect.bottom;
340                 }
341                 
342                 for (i = 0; i < sNumMonitors; i++)
343                 {
344                         Rect r = sMonitors[i].origRect;
345                         Rect r2 = r;
346                         
347                         // normalize rect and scale
348                         OffsetRect(&r, -r.left, -r.top);
349                         r.bottom = (float)r.bottom * scale;
350                         r.right = (float)r.right * scale;
351                         
352                         // offset rect wrt gray region
353                         OffsetRect(&r, (float)(r2.left - origGrayRect.left) * scale, 
354                                                         (float)(r2.top - origGrayRect.top) * scale);
355
356                         sMonitors[i].scaledRect = r;
357                 }
358                 
359                 // center scaledGrayRect in the pane
360                 OffsetRect(&scaledGrayRect, (paneRect.right - scaledGrayRect.right) / 2,
361                                         (paneRect.bottom - scaledGrayRect.bottom) / 2);
362
363                 // offset monitors to match
364                 for (i = 0; i < sNumMonitors; i++)
365                         OffsetRect(&sMonitors[i].scaledRect, scaledGrayRect.left, scaledGrayRect.top);
366         }
367         else
368                 return paramErr;
369                 
370         // setup the procs for the pick monitor user pane
371         err = SetupUserPaneProcs(inPane, drawProc, hitTestProc, trackingProc);
372         return err;
373 }
374
375
376 //-----------------------------------------------------------------------------
377 //      TearDownPickMonitorPane
378 //-----------------------------------------------------------------------------
379 //      Disposes of everything associated with the Pick Monitor pane. You should
380 //      call this when disposing the dialog.
381
382 OSErr TearDownPickMonitorPane(ControlRef inPane)
383 {
384         OSErr err;
385         err = DisposeUserPaneProcs(inPane);
386         sNumMonitors = 0;
387         return err;
388 }
389
390 #pragma mark -
391
392 //------------------------------------------------------------------------------------
393 // ¥ PickMonitorHandler
394 //------------------------------------------------------------------------------------
395 // Our command handler for the PickMonitor dialog.
396
397 static pascal OSStatus PickMonitorHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* inUserData )
398 {
399         #pragma unused( inHandler )
400         
401         HICommand                       cmd;
402         OSStatus                        result = eventNotHandledErr;
403         WindowRef                       theWindow = (WindowRef)inUserData;
404
405         // The direct object for a 'process commmand' event is the HICommand.
406         // Extract it here and switch off the command ID.
407
408         GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd );
409
410         switch ( cmd.commandID )
411         {
412                 case kHICommandOK:                      
413                         QuitAppModalLoopForWindow( theWindow );
414                         result = noErr;
415                         break;
416                 
417                 case kHICommandCancel:                  
418                         // Setting sSelectedDevice to zero will signal that the user cancelled.
419                         sSelectedDevice = 0;
420                         QuitAppModalLoopForWindow( theWindow );
421                         result = noErr;
422                         break;
423
424         }       
425         return result;
426 }
427
428
429 #pragma mark -
430
431 //-----------------------------------------------------------------------------
432 // CanUserPickMonitor
433 //-----------------------------------------------------------------------------
434 // Returns true if more than one monitor is available to choose from.
435
436 Boolean CanUserPickMonitor (void)
437 {
438         GDHandle dev = GetDeviceList();
439         OSErr err = noErr;
440         int numMonitors;
441         
442         // build the list of monitors
443         numMonitors = 0;
444         while (dev && numMonitors < kMaxMonitors)
445         {
446                 if (TestDeviceAttribute(dev, screenDevice) && TestDeviceAttribute(dev, screenActive))
447                 {
448                         numMonitors++;
449                 }
450                 dev = GetNextDevice(dev);
451         }
452
453         if (numMonitors > 1) return true;
454         else return false;
455 }
456
457 //-----------------------------------------------------------------------------
458 // PickMonitor
459 //-----------------------------------------------------------------------------
460 // Prompts for a monitor. Returns userCanceledErr if the user cancelled.
461
462 OSStatus PickMonitor (DisplayIDType *inOutDisplayID, WindowRef parentWindow)
463 {
464         WindowRef theWindow;
465         OSStatus status = noErr;
466         static const ControlID  kUserPane               = { 'MONI', 1 };
467         
468         // Fetch the dialog
469
470         IBNibRef aslNib;
471         CFBundleRef theBundle = CFBundleGetMainBundle();
472         status = CreateNibReferenceWithCFBundle(theBundle, CFSTR("ASLCore"), &aslNib);
473         status = ::CreateWindowFromNib(aslNib, CFSTR( "Pick Monitor" ), &theWindow );
474         if (status != noErr)
475         {
476                 assert(false);
477                 return userCanceledErr;
478         }
479
480 #if 0
481         // Put game name in window title. By default the title includes the token <<<kGameName>>>.
482
483         Str255 windowTitle;
484         GetWTitle(theWindow, windowTitle);
485         FormatPStringWithGameName(windowTitle);
486         SetWTitle(theWindow, windowTitle);
487 #endif
488                 
489         // Set up the controls
490
491         ControlRef monitorPane;
492         GetControlByID( theWindow, &kUserPane, &monitorPane );
493         assert(monitorPane);
494
495         SetupPickMonitorPane(monitorPane, *inOutDisplayID);
496
497         // Create our UPP and install the handler.
498
499         EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess };
500         EventHandlerUPP handler = NewEventHandlerUPP( PickMonitorHandler );
501         InstallWindowEventHandler( theWindow, handler, 1, &cmdEvent, theWindow, NULL );
502         
503         // Show the window
504
505         if (parentWindow)
506                 ShowSheetWindow( theWindow, parentWindow );
507         else
508                 ShowWindow( theWindow );
509
510         // Now we run modally. We will remain here until the PrefHandler
511         // calls QuitAppModalLoopForWindow if the user clicks OK or
512         // Cancel.
513
514         RunAppModalLoopForWindow( theWindow );
515
516         // OK, we're done. Dispose of our window and our UPP.
517         // We do the UPP last because DisposeWindow can send out
518         // CarbonEvents, and we haven't explicitly removed our
519         // handler. If we disposed the UPP, the Toolbox might try
520         // to call it. That would be bad.
521
522         TearDownPickMonitorPane(monitorPane);
523         if (parentWindow)
524                 HideSheetWindow( theWindow );
525         DisposeWindow( theWindow );
526         DisposeEventHandlerUPP( handler );
527
528         // Return settings to caller
529
530         if (sSelectedDevice != 0)
531         {
532                 // Read back the controls
533                 DMGetDisplayIDByGDevice (sSelectedDevice, &*inOutDisplayID, true);
534                 return noErr;
535         }
536         else
537                 return userCanceledErr;
538
539 }
540