68k Determining if screen is set to color vs b&w at runtime in Sys 7

Relating to a 68k application

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
183
43
Wondering how to determine current color depth in System 6 and 7.
For example - if my app wants to know if it should show color or not - it should check something no doubt that tells it whether the current screen (GDevice likely) has color or not as well as what bit depth it's set to.

Best I can tell from Inside Macintosh - imaging with Quickdraw -pg 5-32, is the TestDeviceAttribute(...) function as in:
Code:
void BeepIfYouHaveColor()
{
 GDHandle screen;
 Boolean gColorScreen;
 screen = GetMainDevice();
 gColorScreen = TestDeviceAttribute(screen,gdDevType);
 if(true == gColorScreen) 
  SysBeep(10);
}

My understanding of this from Inside Macintosh is that this should test for the gDevType flag on the main screen (e.g. if it's a color monitor, set to colors, gColorScreen should be True but if it is a b&w monitor or a color monitor set to b&w, it should return False).
HOWEVER -- when I run this, I get True no matter whether my color monitor is set to color or B&W -- so it's like it's checking if the screen supports color as opposed to whether it's set to color presently which is what I want...

Anyone have a little more experience with this function?
 

joevt

Tinkerer
Mar 5, 2023
73
32
18
There could be multiple displays connected with different color depths. A window could span multiple displays. GetMainDevice is not necessarily the device with the highest color depth. I forget how drawing and updating should be handled when a window spans multiple displays. Do you get a separate update event for each display the window intersects? Or do you have to loop through each device, set the clip region to the intersection of the device and the window, and then draw for that region?
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
True, but the main device is by definition the device with the menu bar, which is where the user expects “most stuff“ to happen by default, so it’s usually the best choice for “most stuff“.

No, you get a single update event for any window. Most of the time you’re not really supposed to care about different color depths – just draw, and QD will handle things for you. In the cases where you do need to care about color depths, there is a routine called DeviceLoop that will call you back once for each device a given region intersects.

Handling all the edge cases involving multiple screens is, at least for me, “work not fun”, and since in the present day the whole point of vintage Mac development is supposed to be fun, in most cases I recommend avoiding all these mechanics, and just using the main device for most purposes. Good question though.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
Oh and if you just want the device with the biggest color depth, you can call GetMaxDevice instead of GetMainDevice. But then my question (again in 2023, not so much 1988) would be … why didn’t the user just put the menu bar over there?
 

Patrick

Tinkerer
Oct 26, 2021
434
1
224
43
as you said, the user expectation is that new windows pop up on the one with the menubar. So its prolly best to stick to what most apps did ? just to be consistent.
I remember most apps just yelled at me if i had the "wrong bit debth" set. but a few would be able to switch to the one they wanted, then switch back when exited. I guess this auto switch would only make sense in a context of a game where it can be reasonable to be the only or main thing running while playing.
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
183
43
True, but the main device is by definition the device with the menu bar, which is where the user expects “most stuff“ to happen by default, so it’s usually the best choice for “most stuff“.

No, you get a single update event for any window. Most of the time you’re not really supposed to care about different color depths – just draw, and QD will handle things for you. In the cases where you do need to care about color depths, there is a routine called DeviceLoop that will call you back once for each device a given region intersects.

Handling all the edge cases involving multiple screens is, at least for me, “work not fun”, and since in the present day the whole point of vintage Mac development is supposed to be fun, in most cases I recommend avoiding all these mechanics, and just using the main device for most purposes. Good question though.
Yes. Inside Macintosh’s QuickDraw book explains this “DeviceLoop” callback system. It seems to be the mechanism for an app to span multiple graphics devices even if tthey have different bit depth etc.
 

joevt

Tinkerer
Mar 5, 2023
73
32
18
True, but the main device is by definition the device with the menu bar, which is where the user expects “most stuff“ to happen by default, so it’s usually the best choice for “most stuff“.

No, you get a single update event for any window. Most of the time you’re not really supposed to care about different color depths – just draw, and QD will handle things for you. In the cases where you do need to care about color depths, there is a routine called DeviceLoop that will call you back once for each device a given region intersects.

Handling all the edge cases involving multiple screens is, at least for me, “work not fun”, and since in the present day the whole point of vintage Mac development is supposed to be fun, in most cases I recommend avoiding all these mechanics, and just using the main device for most purposes. Good question though.
Oh right, DeviceLoop. I remember that now.

Controls and window structures will appear different on color and B&W displays. CDEFs and WDEFs probably use DeviceLoop.

You would use DeviceLoop for your own drawing if you want what you draw to be different depending on color depth.
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
183
43
Don’t use that function. Check the pixelSize field of (**GetMainDevice()).gdPMap. if it’s greater than one, you’ve got color right now.
Thanks again for this awesome clue.

Following up on trying it:
gdPMap is a pixelMap object which has many fields itself (see Inside Macintosh - Imaging with Quickdraw - Pg 4-10)
1689183869731.png

the **pixelSize** field is what I'm *guessing* you had in mind, @Crutch , but let me know if that's wrong.

So see the updated sample code I'm making:
Code:
void SystemIdentify()
{   
 GDHandle screen;
 screen = GetMainDevice();
 gPixelBitDepth = (**(**screen).gdPMap).pixelSize;//TestDeviceAttribute(screen,gdDevType);
 if(1 < gPixelBitDepth) SysBeep(10);
}

This function will beep if the monitor is set to anything higher than black & white. Note the double dereference since gdPMap is itself a handle just like screen is a handle (though to different structs).



In any case - this gets one most of the way there. Note that it tells you the # bits per pixel that the screen is **currently** set to. This gets me almost where I want to be. Note that "4 grays" and "4 colors" both have the same pixelSize of 2. Similarly modes "16 grays" and "16 colors" also have the same pixelSize of 4. But yes -- B&W is the only monitor setting that has a pixelSize of 1. So this definitely will tell you if you are in B&W mode or NOT in B&W mode.

So Cruth deserves full credit for the proposed answer :)

My problem, explained better now, is that I'm looking at the current monitor pixel depth so that I can know which icon from an app to display (which could be a subset of either B&W, 4 bit, or 8-bit color).

I have deeper problems specific to the app I'm working on and so I'll leave this discussion where it's at since I believe this is the best way to find out what the monitor is currently set to -- unless someone can shed some additional light on how to differentiate between 4-color and 4-gray (same for 16-) modes using the above or other methods. For further follow-up pertaining to how I hope to use this information I'll continue to use my app-specific thread on my MacDock app (you can find it in the same forum area as this thread).

Thx all.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
Oh, you shouldn’t need all this for that.

If you just want to plot an application’s icon in the correct bit depth, the routine to use is PlotIconID. It automatically grabs the right ICN#/icl4/icl8 etc for you, and plots it appropriately given the bit depth.

You can look it up in THINK Reference. Also see Tech Note #306.

#include <Icons.h>
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
And yes sorry, device.gdPMap is of course a PixMapHandle which must be double-dereferenced to get to the data, in particular pixelSize.
 

joevt

Tinkerer
Mar 5, 2023
73
32
18
To determine color vs grayscale, For indexed color modes (8 bit or less), can you examine the color table (pmTable) to see if it has any entries that don't have red = green = blue?

Regarding drawing icons, maybe try going to https://developer.apple.com/library/archive/navigation/#section=Platforms&topic=macOS and searching for Icon.

B&W icons: https://developer.apple.com/library/archive/technotes/qd/qd_17.html#//apple_ref/doc/uid/DTS10002730
System 7 color icons: https://developer.apple.com/library/archive/technotes/qd/qd_18.html#//apple_ref/doc/uid/DTS10002731
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
Yes, that last link is TN306 (later remembered QD18) which I referenced above and explains the use of PlotIconID, which is the routine I suggested.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
To determine color vs grayscale, For indexed color modes (8 bit or less), can you examine the color table (pmTable) to see if it has any entries that don't have red = green = blue?

No — again this shouldn’t be necessary if MacOfAllTrades uses the icon utilities I suggested above — but if it’s really necessary, you use TestDeviceAttribute(GetMainDevice(), gdDevType).

If it returns TRUE, the device is set to color — otherwise grayscale.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
By the way, as a general point of coding philosophy, System 7 is a really old dev platform, but it wasn’t awful. If one is writing code and you get stuck and find yourself thinking something like “I need to manually check all the various bit depths of icons that exist for this application vs. the current screen settings” or “I want to know if the monitor is color or grayscale, let me check all the entires in the color table to see if any aren’t gray” … that’s a good time to take a beat and say to yourself, “wait a second, there has to be a Toolbox call for that….”. And 95% of the time there is! (The other 5% of the time is just infuriating…)
 
  • Like
Reactions: MacOfAllTrades

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
183
43
Oh, you shouldn’t need all this for that.

If you just want to plot an application’s icon in the correct bit depth, the routine to use is PlotIconID. It automatically grabs the right ICN#/icl4/icl8 etc for you, and plots it appropriately given the bit depth.

You can look it up in THINK Reference. Also see Tech Note #306.

#include <Icons.h>
Agreed that PlotIconID will do what you say. Inside Macintosh phrases it as "PlotIconID selects the most appropriate icon resource for the current bit depth of the display device and the rectangle in which the icon is to be drawn"
However, PlotIconID(...) takes an integer as the ResourceID parameter -- this is all well and good if the icon you are plotting is in within >your application< but if I'm trying to plot icons from external applications then, as far as I can tell and I have been known to be dead wrong so please let me know if I am, this is of no help in my use case as the icons I'm trying to plot are from external apps and not stored as a resource in my application.

That's how I initially landed on using PBDTGetIcon(...) which works through the Desktop Manager to get an icon and it offers no way to automatically get the 'best icon' for your current screen's setup -- or am I wrong on some of that?
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
Here’s how I would do it using the Icon Utilities routines (Inside Macintosh: More Macintosh Toolbox) — there may be a better way to avoid the explicit resource type mapping in my step 1 below:

- call MakeIconCache, passing it a pointer to a custom icon getter function. Icon getters are handed a resource type based on Icon Utilities’ optimizing for current screen depth etc. Your custom icon getter will then just map that resource type like ‘icl8’ to one of the icon size constants needed by PBDTGetIcon like ‘kLarge8BitIcon’, then call PBDTGetIcon to get the icon, and return the icon handle
- call LoadIconCache, which invokes your custom icon getter function to get the right icon, automatically handing it the correct resource type
- call PlotIconSuite to draw the icon
 
Last edited:

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
Actually you can save a step with PlotIconMethod. This code works for me. (‘Check’ is my own macro to shout if the argument is nonzero, you can replace with your own.)

1689264466508.png


C:
pascal OSErr MakeIconCache (Handle *theCache, ProcPtr GetAnIcon, Ptr
        yourDataPtr ) = {0x303C, 0x0604, 0xABC9};

pascal OSErr LoadIconCache (const Rect *theRect, short alignment, long
        transform, Handle theSuite ) = {0x303C, 0x0606, 0xABC9};

pascal OSErr PlotIconSuite (const Rect *theRect, short alignment,
        short transform, Handle theSuite) = {0x303C, 0x0501, 0xABC9};

pascal OSErr DisposeIconSuite (Handle theSuite, Boolean disposeData)
        = {0x303C, 0x0302, 0xABC9};

pascal OSErr PlotIconMethod (const Rect *r, short alignment, short transform,
            ProcPtr method, Ptr data)
        = {0x303C, 0x0805, 0xABC9};

pascal Handle IconGetter(ResType type, Ptr data)
{
    DTPBRec pb;
    short s, k;
    Handle h;
    short vRefNum;
  
    // open desktop db
  
    Check(GetVol(NULL, &vRefNum));
      
    pb.ioNamePtr = NULL;
    pb.ioVRefNum = vRefNum;
  
    Check(PBDTOpenInform(&pb));

    // get icon info
  
    switch (type)
    {
        case 'ICN#': s = kLargeIconSize;     k = kLargeIcon;     break;
        case 'icl4': s = kLarge4BitIconSize; k = kLarge4BitIcon; break;
        case 'icl8': s = kLarge8BitIconSize; k = kLarge8BitIcon; break;
        case 'ics#': s = kSmallIconSize;     k = kSmallIcon;     break;
        case 'ics4': s = kSmall4BitIconSize; k = kSmall4BitIcon; break;
        case 'ics8': s = kSmall8BitIconSize; k = kSmall8BitIcon; break;

        default: DebugStr("\punknown icon type");
    }

    h = NewHandle(s);
    HLock(h);  // just keep it locked til done

    // pb.ioDTRefNum =    ... already populated
    pb.ioDTReqCount  = s;
    pb.ioDTBuffer    = *h;
    pb.ioIconType    = k;
    pb.ioTagInfo     = 0;
  
    pb.ioFileCreator = 'RSED';  // get icon for ResEdit
    pb.ioFileType    = 'APPL';  // (only works for apps on indicated vRefNum!)

    Check(PBDTGetIconSync(&pb));

    return h;
}


void main()
{
    WindowPtr w;
    const Rect r = { 100, 100, 200, 200};
    const Rect iconRect = { 10, 10, 42, 42 };
  
    InitGraf(&thePort);
    InitFonts();
    InitMenus();
    InitWindows();
    InitDialogs(NULL);

    w = NewCWindow(NULL, &r, "\p", true, documentProc, NULL, true, 0L);
    SetPort(w);
  
    Check(
        PlotIconMethod(&iconRect, 0, 0, IconGetter, NULL)
    );
  
    while (!Button()) ;
}
 
  • Like
Reactions: MacOfAllTrades

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
(Of course an alternative would be to skip the desktop DB and just do what the Finder does: open the application’s resource fork, access its BNDL resource to get the resource ID for the icon suite for type APPL, and then use PlotIconID as I suggested before. But the desktop DB is the “right” way to do this, probably.)