DisplayVideo.c

/*
    File:       DisplayVideo.c
    
    Description:DisplayVideo will display all info about all video modes supported
                by each installed video card with their attached monitor. The purpose
                of this code is to provide a sample of how developers can discover the
                bit depths and timings of multisync displays.
 
    Author:     EWA
    Copyright:  Copyright: © 1995-1999 by Apple Computer, Inc.
                all rights reserved.
    
    Disclaimer: You may incorporate this sample code into your applications without
                restriction, though the sample code has been provided "AS IS" and the
                responsibility for its operation is 100% yours.  However, what you are
                not permitted to do is to redistribute the source as "DSC Sample Code"
                after having made changes. If you're going to re-distribute the source,
                we require that you make it clear in the source that the code was
                descended from Apple Sample Code, but that you've made changes.
    
    Change History (most recent first):
                6/24/99 Updated for Metrowerks Codewarror Pro 2.1(KG)
                1/28/97 Updated source for Metrowerks CodeWarrior 11 Fixed up 
                        formatting and generally made things nicer(EWA)
                5/24/95 New today(EWA)
 
*/
 
 
#include <Dialogs.h>
#include <Devices.h>
#include <Displays.h>
#include <Errors.h>
#include <FixMath.h>
#include <fp.h>
#include <Gestalt.h>
#include <Memory.h>
#include <Palettes.h>
#include <PLStringFuncs.h>
#include <QuickDraw.h>
#include <ROMDefs.h>
#include <Slots.h>
#include <StdIO.h>
#include <Video.h>
#include <TextUtils.h>
#include <Strings.h>
#include <DriverServices.h>
 
#include <stdlib.h>
 
//--------------------------------------------------------------
//
// Internal defines, structs, typedefs, and routine declarations
//
//--------------------------------------------------------------
struct DepthInfo {
    VDSwitchInfoRec         depthSwitchInfo;            // This is the switch mode to choose this timing/depth
    VPBlock                 depthVPBlock;               // VPBlock (including size, depth and format)
};
typedef struct DepthInfo DepthInfo;
 
struct ListIteratorDataRec {
    unsigned long           displayModeFlags;           // 
    VDSwitchInfoRec         displayModeSwitchInfo;      //
    VDResolutionInfoRec     displayModeResolutionInfo;  //
    VDTimingInfoRec         displayModeTimingInfo;      // Contains timing flags and such
    unsigned long           depthBlockCount;            // How many depths available for a particular timing
    DepthInfo               *depthBlocks;               // Array of DepthInfo
    Str255                  displayModeName;            // name of the timing mode
};
typedef struct ListIteratorDataRec ListIteratorDataRec;
 
void PrintCurrentVideoSetting (GDHandle walkDevice);
 
void DisplayVideoSettings (void);
 
void PrintAvailableVideoSettingsDM1 (GDHandle walkDevice);
 
void PrintAvailableVideoSettingsDM2 (GDHandle walkDevice,
                                DMDisplayModeListIteratorUPP myModeIteratorProc,
                                DMListIndexType theDisplayModeCount,
                                DMListType *theDisplayModeList);
 
pascal void ModeListIterator (  void *userData,
                                DMListIndexType itemIndex,
                                DMDisplayModeListEntryPtr displaymodeInfo);
 
// routine implementations
void main(void)
{
    DisplayVideoSettings ();
}
 
 
//--------------------------------------------------------------
//
// Implementation of sample code
//
//--------------------------------------------------------------
void PrintCurrentVideoSetting (GDHandle walkDevice)
{
    unsigned long       displayMgrVersion;
    OSErr               error = paramErr;
    CntrlParam          pBlock;
    VDSwitchInfoRec     switchInfo;
    AuxDCEHandle        theDCE;
    VDSwitchInfoRec     videoMode;      
 
    Gestalt(gestaltDisplayMgrVers, (long*)&displayMgrVersion);
    if (displayMgrVersion >= 0x00020000)
    {   // get the info the DM 2.0 way
        error = DMGetDisplayMode(walkDevice, &switchInfo);
        if (noErr == error)
        {
            printf ("Current Settings DM2 - displayMode: %d, depthMode: %d\n", switchInfo.csData, switchInfo.csMode);
        }
        return;
    }
    else
    {   // get the info the DM 1.0 way
        videoMode.csMode = -1;      // init to bogus value
        videoMode.csData = -1;      // init to bogus value          
        pBlock.ioNamePtr = nil;
        pBlock.ioCRefNum = (*(walkDevice))->gdRefNum;
        pBlock.csCode = cscGetCurMode;
        *(Ptr *)&pBlock.csParam[0] = (Ptr)&videoMode;
            
        error = PBStatusSync((ParmBlkPtr )&pBlock); // ask the driver first....since we trust it the most
            
        if ( noErr == error && ((-1 == videoMode.csMode) || (-1 == videoMode.csData)) )
            error = statusErr;
        
        if (noErr != error) // if the driver has no clue fill it videoMode by hand as a last resort
        {   
            theDCE = (AuxDCEHandle)GetDCtlEntry((*(walkDevice))->gdRefNum);
            
            if( theDCE )
            {
                videoMode.csData = (unsigned char)(*theDCE)->dCtlSlotId; 
                videoMode.csMode = (*(walkDevice))->gdMode;
                error = noErr;
            }
        }
        if (noErr == error)
        {
            printf ("Current Settings DM1 - displayMode: %d, depthMode: %d\n", videoMode.csData, videoMode.csMode);
        }
        return;
    }
}
 
 
void DisplayVideoSettings (void)
{
    Boolean                         displayMgrPresent;
    short                           iCount = 0;                 // just a counter of GDevices we have seen
    DMDisplayModeListIteratorUPP    myModeIteratorProc = nil;   // for DM2.0 searches
    SpBlock                         spBlock;
    Boolean                         suppliedGDevice;    
    DisplayIDType                   theDisplayID;               // for DM2.0 searches
    DMListIndexType                 theDisplayModeCount;        // for DM2.0 searches
    DMListType                      theDisplayModeList;         // for DM2.0 searches
    long                            value = 0;
    GDHandle                        walkDevice = nil;           // for everybody
 
    Gestalt(gestaltDisplayMgrAttr,&value);
    displayMgrPresent=value&(1<<gestaltDisplayMgrPresent);
    displayMgrPresent=displayMgrPresent && (SVersion(&spBlock)==noErr); // need slot manager
    if (displayMgrPresent)                                              // and Display Manager
    {   
        walkDevice = DMGetFirstScreenDevice (dmOnlyActiveDisplays);         // for everybody
        suppliedGDevice = false;
        
        myModeIteratorProc = NewDMDisplayModeListIteratorProc(ModeListIterator);    // for DM2.0 searches
    
        // Note that we are hosed if somebody changes the gdevice list behind our backs while we are iterating....
        // ...now do the loop if we can start
        if( walkDevice && myModeIteratorProc) do // start the search
        {
            iCount++;       // GDevice we are looking at (just a counter)
            printf("=================================================\n");
            printf("GDevice #%d (0x%X) at location (%d,%d).\n",iCount, *walkDevice, (long )(*walkDevice)->gdRect.left, (long )(*walkDevice)->gdRect.top);
            printf("=================================================\n\n");
 
            PrintCurrentVideoSetting (walkDevice);
            
            if( noErr == DMGetDisplayIDByGDevice( walkDevice, &theDisplayID, false ) )  // DM1.0 does not need this, but it fits in the loop
            {
                theDisplayModeCount = 0;    // for DM2.0 searches
                if (noErr == DMNewDisplayModeList(theDisplayID, 0, 0, &theDisplayModeCount, &theDisplayModeList) )
                {
                    // search NuBus & PCI the new kool way through Display Manager 2.0
                //  printf("\nAvailable Video Settings DM2.0 way\n");
                    PrintAvailableVideoSettingsDM2 (walkDevice, myModeIteratorProc, theDisplayModeCount, &theDisplayModeList);
                    DMDisposeList(theDisplayModeList);  // now toss the lists for this gdevice and go on to the next one
                }
                else
                {
                    // search NuBus only the old disgusting way through the slot manager
                    printf("\nAvailable Video Settings DM1.0 way\n");
                    PrintAvailableVideoSettingsDM1 (walkDevice);
                }
            }
 
            printf("\n\n\n");
 
        } while ( !suppliedGDevice && nil != (walkDevice = DMGetNextScreenDevice ( walkDevice, dmOnlyActiveDisplays )) );   // go until no more gdevices
        if( myModeIteratorProc )
            DisposeRoutineDescriptor(myModeIteratorProc);
        return;
    }
}
 
void PrintAvailableVideoSettingsDM1 (GDHandle walkDevice)
{
    AuxDCEHandle myAuxDCEHandle;
    unsigned long   depthMode;
    unsigned long   displayMode;
    OSErr           error;
    OSErr           errorEndOfTimings;
    short           height;
    short           jCount = 0;
    Boolean         modeOk;
    SpBlock         spAuxBlock;
    SpBlock         spBlock;
    unsigned long   switchFlags;
    VPBlock         *vpData;
    short           width;
 
    myAuxDCEHandle = (AuxDCEHandle) GetDCtlEntry((**walkDevice).gdRefNum);  
    spBlock.spSlot = (**myAuxDCEHandle).dCtlSlot;
    spBlock.spID = (**myAuxDCEHandle).dCtlSlotId;
    spBlock.spExtDev = (**myAuxDCEHandle).dCtlExtDev;
    spBlock.spHwDev = 0;                                // we are going to get this pup
    spBlock.spParamData = 1<<foneslot;                  // this slot, enabled, and it better be here.
    spBlock.spTBMask = 3;                               // don't have constants for this yet
    errorEndOfTimings = SGetSRsrc(&spBlock);            // get the spDrvrHW so we know the ID of this puppy. This is important
                                                        // since some video cards support more than one display, and the spDrvrHW
                                                        // ID can, and will, be used to differentiate them.
    
    if ( noErr == errorEndOfTimings )
    {
        // reinit the param block for the SGetTypeSRsrc loop, keep the spDrvrHW we just got
        spBlock.spID = 0;                               // start at zero, 
        spBlock.spTBMask = 2;                           // 0b0010 - ignore DrvrSW - why ignore the SW side? Is it not important for video?
        spBlock.spParamData = (1<<fall) + (1<<foneslot) + (1<<fnext);   // 0b0111 - this slot, enabled or disabled, so we even get 640x399 on Blackbird
        spBlock.spCategory=catDisplay;
        spBlock.spCType=typeVideo;
        errorEndOfTimings = SGetTypeSRsrc(&spBlock);    // but only on 7.0 systems, not a problem since we require DM1.0
        
        // now, loop through all the timings for this GDevice
        if ( noErr == errorEndOfTimings ) do
        {
            // now, loop through all possible depth modes for this timing mode
            displayMode = (unsigned char)spBlock.spID;  // "timing mode, ie:resource ref number"
            for (jCount = firstVidMode; jCount<= sixthVidMode; jCount++)
            {
                depthMode = jCount;     // vid mode
                error = DMCheckDisplayMode(walkDevice,displayMode,depthMode,&switchFlags,0,&modeOk);
    
                // only if the mode okay
                if (noErr == error && modeOk)
                {
                    // have a good displayMode/depthMode combo - now lets look inside
                    spAuxBlock = spBlock;               // don't ruin the iteration spBlock!!
                    spAuxBlock.spID = depthMode;        // vid mode
                    error=SFindStruct(&spAuxBlock);     // get back a new spsPointer
                    if (noErr == error)                 // keep going if no errorÉ
                    {
                        spAuxBlock.spID = 0x01;         // mVidParams request
                        error=SGetBlock (&spAuxBlock);  // use the new spPointer and get back...a NewPtr'ed spResult
                        if (noErr == error)             // Ékeep going if no errorÉ
                        {                               // We have data! lets have a look
                            vpData = (VPBlock*)spAuxBlock.spResult;
                            height = vpData->vpBounds.bottom;   // left and top are usually zero
                            width = vpData->vpBounds.right;
                            
                            // print screen data
                            printf("\nTiming Mode: %d\n", displayMode);
                            printf("Depth Mode: %d, Depth: %d, Resolution: %dH x %dV\n",
                                depthMode,
                                vpData->vpPixelSize,
                                vpData->vpBounds.right,
                                vpData->vpBounds.bottom);
                            printf("Components per Pixel: %d, bits per Component: %d\n",
                                vpData->vpCmpCount,
                                vpData->vpCmpSize);
                            printf("Switch flags:\n");
                            if (switchFlags & 1<<kNoSwitchConfirmBit)
                                printf("      Confirmation not required,\n");
                            else
                                printf("      Confirmation required,\n");
                            if (switchFlags & 1<<kDepthNotAvailableBit)
                                printf("      Current depth not available in this mode,\n");
                            else
                                printf("      Current depth available in this mode,\n");
                            if (switchFlags & 1<<kShowModeBit)
                                printf("      Always shown,\n");
                            else
                                printf("      Not always shown,\n");
                            if (switchFlags & 1<<kModeNotResizeBit)
                                printf("      Not resizeable,\n");
                            else
                                printf("      Resizeable\n");
 
                            if (spAuxBlock.spResult) DisposePtr ((Ptr)spAuxBlock.spResult); // toss this puppy when done
                        }
                    }
                }
            }
            // go around again, looking for timing modes for this GDevice
            spBlock.spTBMask = 2;       // ignore DrvrSW
            spBlock.spParamData =  (1<<fall) + (1<<foneslot) + (1<<fnext);  // next resource, this slot, whether enabled or disabled
            errorEndOfTimings = SGetTypeSRsrc(&spBlock);    // and get the next timing mode
        } while ( noErr == errorEndOfTimings ); // until the end of this GDevice
    }
 
}
 
pascal void ModeListIterator(void *userData, DMListIndexType, DMDisplayModeListEntryPtr displaymodeInfo)
{
    unsigned long           depthCount;
    short                   iCount;
    ListIteratorDataRec     *myIterateData      = (ListIteratorDataRec*) userData;
    DepthInfo               *myDepthInfo;
    
// printf ("\n now in ModeListIterator\n");
    // set display data in a round about way
    // Set the basics
    myIterateData->displayModeFlags             = displaymodeInfo->displayModeFlags;        // Info on this particular display mode
    myIterateData->displayModeSwitchInfo        = *displaymodeInfo->displayModeSwitchInfo;  // not needed - depth info has this per depth
    myIterateData->displayModeResolutionInfo    = *displaymodeInfo->displayModeResolutionInfo;  // refresh rate, pixels/lines at max depth 
    myIterateData->displayModeTimingInfo        = *displaymodeInfo->displayModeTimingInfo;  // to get the flags on timing mode
    PStrCopy ((StringPtr)&myIterateData->displayModeName, (ConstStr255Param)*displaymodeInfo->displayModeName); // the name of the mode
    
    // now get the DMDepthInfo into memory we own
    depthCount = displaymodeInfo->displayModeDepthBlockInfo->depthBlockCount;
    myDepthInfo = (DepthInfo*)NewPtrClear(depthCount * sizeof(DepthInfo));
 
    // set the info for the caller
    myIterateData->depthBlockCount = depthCount;
    myIterateData->depthBlocks = myDepthInfo;
 
    // and fill out all the entries
    if (depthCount) for (iCount=0; iCount < depthCount; iCount++)
    {
        myDepthInfo[iCount].depthSwitchInfo = 
            *displaymodeInfo->displayModeDepthBlockInfo->depthVPBlock[iCount].depthSwitchInfo;
        myDepthInfo[iCount].depthVPBlock = 
            *displaymodeInfo->displayModeDepthBlockInfo->depthVPBlock[iCount].depthVPBlock;
    }
}
 
void PrintAvailableVideoSettingsDM2 (GDHandle walkDevice,
                            DMDisplayModeListIteratorUPP myModeIteratorProc,
                            DMListIndexType theDisplayModeCount,
                            DMListType *theDisplayModeList)
{
    short                   jCount;
    short                   kCount;
    ListIteratorDataRec     searchData;
    double_t                refreshRate;
    unsigned long           switchFlags;
    Boolean                 modeOk;
    OSErr                   error;
 
    searchData.depthBlocks = nil;
    // get the mode lists for this GDevice
// printf ("\n about to start DMGetIndexedDisplayModeFromList loop\n");
    for (jCount=0; jCount<theDisplayModeCount; jCount++)        // get info on all the resolution timings
    {
        DMGetIndexedDisplayModeFromList(*theDisplayModeList, jCount, 0, myModeIteratorProc, &searchData);
// printf ("\n just did a DMGetIndexedDisplayModeFromList\n");
        
        // only if the mode is valid
//      if  (searchData.displayModeTimingInfo.csTimingFlags & 1<<kModeValid)
        {
            // for all the depths for this resolution timing (mode)...
            printf("\nTiming mode: %d (or  0x%X) named Ò%sÓ\n",
                        searchData.depthBlocks[0].depthSwitchInfo.csData,
                        searchData.depthBlocks[0].depthSwitchInfo.csData,
                        P2CStr(searchData.displayModeName));
        
            refreshRate = Fix2X (searchData.displayModeResolutionInfo.csRefreshRate);
            refreshRate = round (refreshRate);
            if (refreshRate == 0)
                printf ("Refresh rate: 0 (not defined in displayModeResolutionInfo.csRefreshRate)\n");
            else
                printf ("Refresh rate: %g\n", refreshRate);
            
            if (searchData.displayModeResolutionInfo.csResolutionFlags & 1<<kResolutionHasMultipleDepthSizes)
                printf("DisplayMode has different H&V per bit depth\n");
            else
                printf("DisplayMode does not have different H&V per bit depth\n");
 
            {
                char        tempArr[6];
                ResType*    tempPtr = (ResType* )&tempArr[0];                   // Make a convenient ptr to assign the restype
                *tempPtr = searchData.displayModeTimingInfo.csTimingFormat;     // contents of string are the resType
                tempArr[4] = 0;                                                 // null temp the string
                printf("Timing format: Ò%sÓ. Timing csData %d\n", tempArr, searchData.displayModeTimingInfo.csTimingData);
            }
            
            printf("Available depths:\n");
 
 
            if (searchData.depthBlockCount) for (kCount = 0; kCount < searchData.depthBlockCount; kCount++)
            {
                // print all the timing information 
                printf("    Depth: %d, Depth mode: 0x%X, Resolution: %dH x %dV\n",
                    searchData.depthBlocks[kCount].depthVPBlock.vpPixelSize,
                    searchData.depthBlocks[kCount].depthSwitchInfo.csMode,
                    searchData.depthBlocks[kCount].depthVPBlock.vpBounds.right,
                    searchData.depthBlocks[kCount].depthVPBlock.vpBounds.bottom);
        //      printf("Components per Pixel: %d, bits per Component: %d\n",
        //          searchData.depthBlocks[kCount].depthVPBlock.vpCmpCount,
        //          searchData.depthBlocks[kCount].depthVPBlock.vpCmpSize);
                    
 
            }
            
            error = DMCheckDisplayMode(walkDevice,
                        searchData.depthBlocks[0].depthSwitchInfo.csData,
                        searchData.depthBlocks[0].depthSwitchInfo.csMode,
                        &switchFlags,
                        0,
                        &modeOk);
                        
            if (noErr == error && modeOk)
            {
                printf("Switch flags = 0x%X\n", switchFlags);
                if (switchFlags & 1<<kNoSwitchConfirmBit)
                    printf("      Confirmation not required,\n");
                else
                    printf("      Confirmation required,\n");
                if (switchFlags & 1<<kDepthNotAvailableBit)
                    printf("      Current depth not available in this mode,\n");
                else
                    printf("      Current depth available in this mode,\n");
                if (switchFlags & 1<<kShowModeBit)
                    printf("      Always shown,\n");
                else
                    printf("      Not always shown,\n");
                if (switchFlags & 1<<kModeNotResizeBit)
                    printf("      Not resizeable,\n");
                else
                    printf("      Resizeable\n");
            }
 
            // timing flags
            printf("Timing flags = 0x%X\n",searchData.displayModeTimingInfo.csTimingFlags);
            if (searchData.displayModeTimingInfo.csTimingFlags & 1<<kModeValid)
                printf("      Valid,\n",searchData.displayModeTimingInfo.csTimingFlags);
            else
                printf("      Invalid,\n",searchData.displayModeTimingInfo.csTimingFlags);
            if (searchData.displayModeTimingInfo.csTimingFlags & 1<<kModeSafe)
                printf("      Safe,\n");
            else
                printf("      Unsafe,\n");
                    if (searchData.displayModeTimingInfo.csTimingFlags & 1<<kModeDefault)
                printf("      Default,\n");
            else
                printf("      Not default,\n");
                    if (searchData.displayModeTimingInfo.csTimingFlags & 1<<kModeShowNow)
                printf("      Always shown,\n");
            else
                printf("      Not always shown,\n");
            if (searchData.displayModeTimingInfo.csTimingFlags & 1<<kModeNotResize)
                printf("      Not resizeable,\n");
            else
                printf("      Resizeable,\n");
            if (searchData.displayModeTimingInfo.csTimingFlags & 1<<kModeRequiresPan)
                printf("      Requires pan.\n");
            else
                printf("      No pan.\n");
                
            // mode flags
            printf("Mode flags = 0x%X\n",searchData.displayModeFlags);
            if (searchData.displayModeFlags & 1<<0)
                printf("      Stripped,\n");
            else
                printf("      Not Stripped,\n");
        }
 
        if (searchData.depthBlocks)
        {
            DisposePtr ((Ptr)searchData.depthBlocks);   // toss for this timing mode of this gdevice
            searchData.depthBlocks = nil;               // init it just so we know
        }
    }
}