dhewm3/neo/sys/osx/PickMonitor.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

526 lines
16 KiB
C++
Raw Blame History

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "PickMonitor.h"
//====================================================================================
// CONSTANTS
//====================================================================================
#define kMaxMonitors 16
//====================================================================================
// TYPES
//====================================================================================
typedef struct
{
GDHandle device;
Rect origRect;
Rect scaledRect;
int isMain;
}
Monitor;
//====================================================================================
// GLOBALS
//====================================================================================
static GDHandle sSelectedDevice;
static int sNumMonitors;
static Monitor sMonitors[kMaxMonitors];
static RGBColor rgbBlack = { 0x0000, 0x0000, 0x0000 };
static RGBColor rgbWhite = { 0xffff, 0xffff, 0xffff };
static RGBColor rgbGray = { 0x5252, 0x8A8A, 0xCCCC }; // this is the blue used in the Displays control panel
//====================================================================================
// MACROS
//====================================================================================
#undef PtInRect
#undef OffsetRect
#undef InsetRect
#undef EraseRect
#undef MoveTo
#undef LineTo
//====================================================================================
// IMPLEMENTATION
//====================================================================================
//-----------------------------------------------------------------------------
// SetupUserPaneProcs
//-----------------------------------------------------------------------------
// Call this to initialize the specified user pane control before displaying
// the dialog window. Pass NULL for any user pane procs you don't need to install.
OSErr SetupUserPaneProcs( ControlRef inUserPane,
ControlUserPaneDrawProcPtr inDrawProc,
ControlUserPaneHitTestProcPtr inHitTestProc,
ControlUserPaneTrackingProcPtr inTrackingProc)
{
OSErr err = noErr;
ControlUserPaneDrawUPP drawUPP;
ControlUserPaneHitTestUPP hitTestUPP;
ControlUserPaneTrackingUPP trackingUPP;
if (0 == inUserPane) return paramErr;
if (inDrawProc && noErr == err)
{
drawUPP = NewControlUserPaneDrawUPP(inDrawProc);
if (0 == drawUPP)
err = memFullErr;
else
err = SetControlData( inUserPane,
kControlEntireControl,
kControlUserPaneDrawProcTag,
sizeof(ControlUserPaneDrawUPP),
(Ptr)&drawUPP);
}
if (inHitTestProc && noErr == err)
{
hitTestUPP = NewControlUserPaneHitTestUPP(inHitTestProc);
if (0 == hitTestUPP)
err = memFullErr;
else
err = SetControlData( inUserPane,
kControlEntireControl,
kControlUserPaneHitTestProcTag,
sizeof(ControlUserPaneHitTestUPP),
(Ptr)&hitTestUPP);
}
if (inTrackingProc && noErr == err)
{
trackingUPP = NewControlUserPaneTrackingUPP(inTrackingProc);
if (0 == trackingUPP)
err = memFullErr;
else
err = SetControlData( inUserPane,
kControlEntireControl,
kControlUserPaneTrackingProcTag,
sizeof(ControlUserPaneTrackingUPP),
(Ptr)&trackingUPP);
}
return err;
}
//-----------------------------------------------------------------------------
// DisposeUserPaneProcs
//-----------------------------------------------------------------------------
// Call this to clean up when you're done with the specified user pane control.
OSErr DisposeUserPaneProcs(ControlRef inUserPane)
{
ControlUserPaneDrawUPP drawUPP;
ControlUserPaneHitTestUPP hitTestUPP;
ControlUserPaneTrackingUPP trackingUPP;
Size actualSize;
OSErr err;
err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneDrawProcTag, sizeof(ControlUserPaneDrawUPP), (Ptr)&drawUPP, &actualSize);
if (err == noErr) DisposeControlUserPaneDrawUPP(drawUPP);
err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneHitTestProcTag, sizeof(ControlUserPaneHitTestUPP), (Ptr)&hitTestUPP, &actualSize);
if (err == noErr) DisposeControlUserPaneHitTestUPP(hitTestUPP);
err = GetControlData(inUserPane, kControlEntireControl, kControlUserPaneTrackingProcTag, sizeof(ControlUserPaneTrackingUPP), (Ptr)&trackingUPP, &actualSize);
if (err == noErr) DisposeControlUserPaneTrackingUPP(trackingUPP);
return noErr;
}
//-----------------------------------------------------------------------------
// drawProc
//-----------------------------------------------------------------------------
// Custom drawProc for our UserPane control.
static pascal void drawProc(ControlRef, SInt16)
{
int i;
RGBColor saveForeColor;
RGBColor saveBackColor;
PenState savePenState;
GetForeColor(&saveForeColor);
GetBackColor(&saveBackColor);
GetPenState(&savePenState);
RGBForeColor(&rgbBlack);
RGBBackColor(&rgbWhite);
PenNormal();
for (i = 0; i < sNumMonitors; i++)
{
RGBForeColor(&rgbGray);
PaintRect(&sMonitors[i].scaledRect);
if (sMonitors[i].isMain)
{
Rect r = sMonitors[i].scaledRect;
InsetRect(&r, 1, 1);
r.bottom = r.top + 6;
RGBForeColor(&rgbWhite);
PaintRect(&r);
RGBForeColor(&rgbBlack);
PenSize(1,1);
MoveTo(r.left, r.bottom);
LineTo(r.right, r.bottom);
}
if (sMonitors[i].device == sSelectedDevice)
{
PenSize(3,3);
RGBForeColor(&rgbBlack);
FrameRect(&sMonitors[i].scaledRect);
}
else
{
PenSize(1,1);
RGBForeColor(&rgbBlack);
FrameRect(&sMonitors[i].scaledRect);
}
}
// restore the original pen state and colors
RGBForeColor(&saveForeColor);
RGBBackColor(&saveBackColor);
SetPenState(&savePenState);
}
//-----------------------------------------------------------------------------
// hitTestProc
//-----------------------------------------------------------------------------
// Custom hitTestProc for our UserPane control.
// This allows FindControlUnderMouse() to locate our control, which allows
// ModalDialog() to call TrackControl() or HandleControlClick() for our control.
static pascal ControlPartCode hitTestProc(ControlRef inControl, Point inWhere)
{
// return a valid part code so HandleControlClick() will be called
return kControlButtonPart;
}
//-----------------------------------------------------------------------------
// trackingProc
//-----------------------------------------------------------------------------
// Custom trackingProc for our UserPane control.
// This won't be called for our control unless the kControlHandlesTracking feature
// bit is specified when the userPane is created.
static pascal ControlPartCode trackingProc (
ControlRef inControl,
Point inStartPt,
ControlActionUPP)
{
int i;
for (i = 0; i < sNumMonitors; i++)
{
if (PtInRect(inStartPt, &sMonitors[i].scaledRect))
{
if (sMonitors[i].device != sSelectedDevice)
{
sSelectedDevice = sMonitors[i].device;
DrawOneControl(inControl);
}
break;
}
}
return kControlNoPart;
}
//-----------------------------------------------------------------------------
// SetupPickMonitorPane
//-----------------------------------------------------------------------------
// Call this to initialize the user pane control that is the Pick Monitor
// control. Pass the ControlRef of the user pane control and a display ID
// for the monitor you want selected by default (pass 0 for the main monitor).
// Call this function before displaying the dialog window.
OSErr SetupPickMonitorPane(ControlRef inPane, DisplayIDType inDefaultMonitor)
{
GDHandle dev = GetDeviceList();
OSErr err = noErr;
// make the default monitor the selected device
if (inDefaultMonitor)
DMGetGDeviceByDisplayID(inDefaultMonitor, &sSelectedDevice, true);
else
sSelectedDevice = GetMainDevice();
// build the list of monitors
sNumMonitors = 0;
while (dev && sNumMonitors < kMaxMonitors)
{
if (TestDeviceAttribute(dev, screenDevice) && TestDeviceAttribute(dev, screenActive))
{
sMonitors[sNumMonitors].device = dev;
sMonitors[sNumMonitors].origRect = (**dev).gdRect;
sMonitors[sNumMonitors].isMain = (dev == GetMainDevice());
sNumMonitors++;
}
dev = GetNextDevice(dev);
}
// calculate scaled rects
if (sNumMonitors)
{
Rect origPaneRect, paneRect;
Rect origGrayRect, grayRect, scaledGrayRect;
float srcAspect, dstAspect, scale;
int i;
GetControlBounds(inPane, &origPaneRect);
paneRect = origPaneRect;
OffsetRect(&paneRect, -paneRect.left, -paneRect.top);
GetRegionBounds(GetGrayRgn(), &origGrayRect);
grayRect = origGrayRect;
OffsetRect(&grayRect, -grayRect.left, -grayRect.top);
srcAspect = (float)grayRect.right / (float)grayRect.bottom;
dstAspect = (float)paneRect.right / (float)paneRect.bottom;
scaledGrayRect = paneRect;
if (srcAspect < dstAspect)
{
scaledGrayRect.right = (float)paneRect.bottom * srcAspect;
scale = (float)scaledGrayRect.right / grayRect.right;
}
else
{
scaledGrayRect.bottom = (float)paneRect.right / srcAspect;
scale = (float)scaledGrayRect.bottom / grayRect.bottom;
}
for (i = 0; i < sNumMonitors; i++)
{
Rect r = sMonitors[i].origRect;
Rect r2 = r;
// normalize rect and scale
OffsetRect(&r, -r.left, -r.top);
r.bottom = (float)r.bottom * scale;
r.right = (float)r.right * scale;
// offset rect wrt gray region
OffsetRect(&r, (float)(r2.left - origGrayRect.left) * scale,
(float)(r2.top - origGrayRect.top) * scale);
sMonitors[i].scaledRect = r;
}
// center scaledGrayRect in the pane
OffsetRect(&scaledGrayRect, (paneRect.right - scaledGrayRect.right) / 2,
(paneRect.bottom - scaledGrayRect.bottom) / 2);
// offset monitors to match
for (i = 0; i < sNumMonitors; i++)
OffsetRect(&sMonitors[i].scaledRect, scaledGrayRect.left, scaledGrayRect.top);
}
else
return paramErr;
// setup the procs for the pick monitor user pane
err = SetupUserPaneProcs(inPane, drawProc, hitTestProc, trackingProc);
return err;
}
//-----------------------------------------------------------------------------
// TearDownPickMonitorPane
//-----------------------------------------------------------------------------
// Disposes of everything associated with the Pick Monitor pane. You should
// call this when disposing the dialog.
OSErr TearDownPickMonitorPane(ControlRef inPane)
{
OSErr err;
err = DisposeUserPaneProcs(inPane);
sNumMonitors = 0;
return err;
}
//------------------------------------------------------------------------------------
// <20> PickMonitorHandler
//------------------------------------------------------------------------------------
// Our command handler for the PickMonitor dialog.
static pascal OSStatus PickMonitorHandler( EventHandlerCallRef, EventRef inEvent, void* inUserData )
{
HICommand cmd;
OSStatus result = eventNotHandledErr;
WindowRef theWindow = (WindowRef)inUserData;
// The direct object for a 'process commmand' event is the HICommand.
// Extract it here and switch off the command ID.
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd );
switch ( cmd.commandID )
{
case kHICommandOK:
QuitAppModalLoopForWindow( theWindow );
result = noErr;
break;
case kHICommandCancel:
// Setting sSelectedDevice to zero will signal that the user cancelled.
sSelectedDevice = 0;
QuitAppModalLoopForWindow( theWindow );
result = noErr;
break;
}
return result;
}
#pragma mark -
//-----------------------------------------------------------------------------
// CanUserPickMonitor
//-----------------------------------------------------------------------------
// Returns true if more than one monitor is available to choose from.
Boolean CanUserPickMonitor (void)
{
GDHandle dev = GetDeviceList();
OSErr err = noErr;
int numMonitors;
// build the list of monitors
numMonitors = 0;
while (dev && numMonitors < kMaxMonitors)
{
if (TestDeviceAttribute(dev, screenDevice) && TestDeviceAttribute(dev, screenActive))
{
numMonitors++;
}
dev = GetNextDevice(dev);
}
if (numMonitors > 1) return true;
else return false;
}
//-----------------------------------------------------------------------------
// PickMonitor
//-----------------------------------------------------------------------------
// Prompts for a monitor. Returns userCanceledErr if the user cancelled.
OSStatus PickMonitor (DisplayIDType *inOutDisplayID, WindowRef parentWindow)
{
WindowRef theWindow;
OSStatus status = noErr;
static const ControlID kUserPane = { 'MONI', 1 };
// Fetch the dialog
IBNibRef aslNib;
CFBundleRef theBundle = CFBundleGetMainBundle();
status = CreateNibReferenceWithCFBundle(theBundle, CFSTR("ASLCore"), &aslNib);
status = ::CreateWindowFromNib(aslNib, CFSTR( "Pick Monitor" ), &theWindow );
if (status != noErr)
{
assert(false);
return userCanceledErr;
}
#if 0
// Put game name in window title. By default the title includes the token <<<kGameName>>>.
Str255 windowTitle;
GetWTitle(theWindow, windowTitle);
FormatPStringWithGameName(windowTitle);
SetWTitle(theWindow, windowTitle);
#endif
// Set up the controls
ControlRef monitorPane;
GetControlByID( theWindow, &kUserPane, &monitorPane );
assert(monitorPane);
SetupPickMonitorPane(monitorPane, *inOutDisplayID);
// Create our UPP and install the handler.
EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess };
EventHandlerUPP handler = NewEventHandlerUPP( PickMonitorHandler );
InstallWindowEventHandler( theWindow, handler, 1, &cmdEvent, theWindow, NULL );
// Show the window
if (parentWindow)
ShowSheetWindow( theWindow, parentWindow );
else
ShowWindow( theWindow );
// Now we run modally. We will remain here until the PrefHandler
// calls QuitAppModalLoopForWindow if the user clicks OK or
// Cancel.
RunAppModalLoopForWindow( theWindow );
// OK, we're done. Dispose of our window and our UPP.
// We do the UPP last because DisposeWindow can send out
// CarbonEvents, and we haven't explicitly removed our
// handler. If we disposed the UPP, the Toolbox might try
// to call it. That would be bad.
TearDownPickMonitorPane(monitorPane);
if (parentWindow)
HideSheetWindow( theWindow );
DisposeWindow( theWindow );
DisposeEventHandlerUPP( handler );
// Return settings to caller
if (sSelectedDevice != 0)
{
// Read back the controls
DMGetDisplayIDByGDevice (sSelectedDevice, &*inOutDisplayID, true);
return noErr;
}
else
return userCanceledErr;
}