mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-30 16:11:11 +00:00
736ec20d4d
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.
526 lines
16 KiB
C++
526 lines
16 KiB
C++
/*
|
||
===========================================================================
|
||
|
||
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;
|
||
|
||
}
|
||
|