ThinkC How to detect of QuickDraw Color is available?

Relating to ThinkC Development

pfuentes69

Active Tinkerer
Oct 27, 2021
380
293
63
Switzerland
Hello,
I checked some docs but didn't find a clear answer...

What is the easiest way to know if the display is B&W or it has colours available?

Txs!
 

Trash80toG4

Active Tinkerer
Apr 1, 2022
919
265
63
Bermuda Triangle, NC USA
Monitor's Control panel I'd think. TattleTech after that. What Mac are you coding for, CPU type's also a dead giveaway. Anything 68000 is single bit B&W. Anything better should have Color QuickDraw in the ROM/toolbox.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
You mean in code right? 😀

If you want to know if it’s possible to get colors, use HasDepth(MainDevice, …) (or you could loop thru all the devices with GetNextDevice and check them all).

If you want to know if it’s currently showing colors, check (**(**MainDevice).gdPMap).pixelSize > 1.
 

pfuentes69

Active Tinkerer
Oct 27, 2021
380
293
63
Switzerland
You mean in code right? 😀

If you want to know if it’s possible to get colors, use HasDepth(MainDevice, …) (or you could loop thru all the devices with GetNextDevice and check them all).

If you want to know if it’s currently showing colors, check (**(**MainDevice).gdPMap).pixelSize > 1.
Thanks, this is to detect in code, so I can adapt my program.
"MainDevice" is not recognised... I guess this is not a global variable and I need to do something to retrieve it?
 

pfuentes69

Active Tinkerer
Oct 27, 2021
380
293
63
Switzerland
I answer myself... Using this it just works:
MainDevice = GetDevice();

Thanks a lot, now I can properly adapt de colours of my window, as some colours were shown as black in B&W
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
MainDevice is a low-memory global. #include <LoMem.h>

or just call GetMainDevice() to access it, as you suggest.
 
Last edited:
  • Like
Reactions: pfuentes69

eric

Administrator
Staff member
Sep 2, 2021
961
1,569
93
MN
scsi.blue
C:
void CAnimCursor::determineTryToUseColor(void)
{
    long theQDVersion;
    OSErr theOSErr;
 
    theOSErr = Gestalt(gestaltQuickdrawVersion, &theQDVersion);
    if (theOSErr == noErr) {
        tryToUseColor = (theQDVersion >= gestalt8BitQD);
    } else {
        SysEnvRec theWorld;
        theOSErr = SysEnvirons(2, &theWorld);
        if (theOSErr == noErr) {
            tryToUseColor = theWorld.hasColorQD;
        } else {
            tryToUseColor = FALSE;
        }
    }
}

Just happened to run into an example in the wild while looking at https://macgui.com/downloads/?file_id=17800&keywords=canimcursor
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
Yep, though you are answering a different question @eric, namely “does this system support Color QuickDraw.”

The answer to that question will be “yes” in many cases (e.g., an SE/30 with no external display) where there is currently no capacity to actually display color. For that, you need HasDepth() (or, if you want to know current screen depth, my pixelSize check given above).
 

BFEXTU

Tinkerer
Jul 15, 2022
177
147
43
Regarding checking the OS environment for the general presence of color (vs. specific gDevice depth), Gestalt calls are often very useful and important to know, but checking certain things via the Trap Manager can sometimes be more efficient/straightforward. Anyway, below is how to do a basic color (not depth) check using the Trap Manager (and @eric 's way is also fine).

C Code for Detecting Color and 32-bit QuickDraw
(incorporate into a class as desired)
C:
//=================================================================
// Prototypes
//=================================================================
Boolean HaveColor(void);
Boolean HaveJackson(void);

/*==================================================================
Function: HaveColor
Parameters: N/A
Description: Determine if any version of Color QuickDraw is present
Returns: True, if there is a color environment
Note: If there is any version of color support, then _OpenCPort exists
(to create a color grafport).
==================================================================*/
Boolean HaveColor(void)
{
    long     badTrap, cqdTrap;

    badTrap = NGetTrapAddress(_Unimplemented,ToolTrap);        // get Unimplemented trap ($A89F)
    cqdTrap = NGetTrapAddress(_OpenCPort,ToolTrap);            // get typical Color QuickDraw trap ($AA00)
    return (cqdTrap != badTrap);                               // return true if Color is implemented
}                                                              // HaveColor

/*==================================================================
Function: HaveJackson
Parameters: N/A
Description: Determine if 32-bit QuickDraw is present
Returns: True, if 32-bit QuickDraw exists
Note: "Jackson Pollock" was the code name for 32-bit QuickDraw.
==================================================================*/
Boolean HaveJackson(void)
{
    long     badTrap, qd32Trap;

    badTrap = NGetTrapAddress(_Unimplemented,ToolTrap);         // get Unimplemented trap ($A89F)
    cqdTrap = NGetTrapAddress(_Jackson,ToolTrap);               // get the Jackson Pollock trap ($AB03)
    return (qd32Trap != badTrap);                               // return true if QD32 is implemented
}                                                               // HaveJackson

ps. ...and similar to 32-bit QuickDraw being Jackson Pollock, QuickTime had the code name "Warhol."
pps. And if for some reason _Jackson is not defined in your traps interface include file, then you can just add a local #define for it (or use later OS headers/libraries).
 
Last edited:

BFEXTU

Tinkerer
Jul 15, 2022
177
147
43
Regarding the mention above of going through all the devices, the way you would do that is to call GetDeviceList(), which returns the first device. Then, you might want to check the gdPMap of that device or do other checks. After that, the next device in the list is stored in "gdNextGD", so you would double dereference the device handle to get to that field. When gdNextGD is nil, you have reached the end of the list. Another reason to walk the device list would be if you want to open your application window on a color device (or the deepest pixel-depth device) in the event that the Mac has more that one gDevice available.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
True, but if you just want the deepest pixel-depth device you can simply skip all that and call GetMaxDevice(). That’s exactly what it’s for.

To elaborate a bit:
1. If you want to know the main device’s current color depth, check (**(**MainDevice).gdPMap).pixelSize as I said above.
2. If you want to know what device currently has the greatest color depth, just use GetMaxDevice(). There is no need to walk thru the device list and check PixMap depths.
3. If you want to know what device has the capability to show the greatest color depth, you need to talk through the device list and check HasDepth() for each.
4. If you want to know if color QuickDraw is supported by software, which tells you nothing about whether you actually have a single color device connected to your Mac, you can use one of the various checks above.

Something that was very common in the ‘90s for games was calling GetMaxDevice() and, if no device currently supported 8-bit color, popping up a dialog saying “you need 256 colors”, making the user go to the Monitors control panel to change the bit depth. This was annoying and lazy. Instead, check HasDepth and pop up a dialog saying “hey I notice your monitor can show 256 colors but it’s not currently configured to display that many, can I change it? I’ll put it back when you quit. Yes/No”. If the user clicks Yes, call SetDepth. Easy peasy for your users.
 
Last edited:

BFEXTU

Tinkerer
Jul 15, 2022
177
147
43
Good general summary.

Here are some additional considerations and reasons for walking the device tree:

1. Calling GetMaxDevice() and then assuming you can or should run on that screen may not necessarily be the right programmatic assumption. Walking the device tree allows you to see what's present and then offer the user the best choice, not just the default one from a high-level call. For example, a user may really want the main device (or your app may want to run on main because of human factors), instead of running on an accessory screen. And, as you say, you can call SetDepth(), as needed. (Clever apps can also call SetEntries() and SetGamma() to manipulate the video output in certain ways.)
2. Besides SetEntries() an app may also need to call SetMode() to toggle color/grayscale.
3. In a multi-monitor system, there may be more than one device that supports the desired depth, but those devices may not be equally capable in other ways, despite having the same pixel depth. For example, graphics acceleration and GWorld support may be relevant/required factors. Or, maybe you want a deep (or any) device that happens to be motherboard/built-in video (maybe for optimal transfer rate). But, you can't assume that any particular device (including motherboard video) is the main/menubar device. The best way to handle this scenario is to walk the tree and offer users choices or include other code that lets your app do the right thing at startup.
4. IIRC, the other problem that can arise is that there can be accessory devices present in the device list that are not yet initialized (not default slot PRAM mode) -- you will see these as grayed-out screens in Monitors. (Monitors can fix these devices just by changing the depth (but it requires more than just a SetDepth() call).
5. Your app may want to run preferentially on certain vendor devices. If that's the case, then you will need to make some Slot Manager calls to read the board sResources -- board or vendor info, etc. You will need the dCtlSlot from the AuxDCE. So, you pass the gdRefNum (same as dCtlRefNum) from the gDevice to a call to GetDCtlEntry(). Assuming it's a slot device, you then get back an AuxDCEHandle (type-cast from the default result of a DCtlHandle) which has the info you would need -- dCtlSlot, dCtlDevBase, etc. -- and then you can make the Slot Manager calls. It takes several steps, but it's not too difficult and will expand your skills!

If your app is simple and you don't really care where it runs, then just use the high level calls and/or add in SetDepth(). If you want to have a better idea of the environment, code defensively and have more control over how and where your app runs, walk the device tree, evaluate what is present, and configure as needed. It's easy - just make some simple utility routines and do whatever is situationally optimal or appropriate.

Apple could have made some of this device interrogation easier via Gestalt and/or other OS/device functionality, and while Gestalt is very useful, it is (or was) also lacking in certain ways for functionality that might have been expected/obviously needed. So, it is incumbent on programmers to make up the difference.

n.b. If you are an MPW user and looking for the definition for low-memory globals, you can find them in SysEqu.h. Other headers of interest are: Slots.h, QDOffscreen.h, Devices.h, and QuickDraw.h. And, as you're learning, it's a good idea to try to become comfortable with parameter blocks and slot/device/driver calls as soon as you're ready for something a little more challenging.