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
182
43
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.)

View attachment 12896

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()) ;
}
A few questions --
My ThinkC 5.0 doesn't seem to have the GetIconMethod(..) function declared in any of its available .h files.
I typed in the definition as you had it though and it seems to work because it does ultimately call my IconGetter function which I can debug with breakpoints.
However -- my IconGetter function's data value doesn't seem to have anything useful in it. I'm making use of it by actually passing a pointer to a structure instance in that last parameter of GetIconMethod(...). I'm just passing a DTPBRec pointer as the data since I have mine already constructed in the function that calls GetIconMethod(...).

I'll keep tinkering but this is very helpful thanks.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
You mean PlotIconMethod right (not GetIconMethod)? Yes, THINK Reference documents (some of) the Icon Utilities routines as not existing in the then-current MPW Universal Headers, so I got this info from Tech Note #306. (The .h files, if they included these routines, would just have the identical definitions, assuming I didn’t make any mistakes.)

You are saying you are passing a ‘data’ argument to PlotIconMethod and your IconGetter function isn’t receiving it? Well that’s weird. Are you sure? I will try ….
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
You mean PlotIconMethod right (not GetIconMethod)? Yes, THINK Reference documents (some of) the Icon Utilities routines as not existing in the then-current MPW Universal Headers, so I got this info from Tech Note #306. (The .h files, if they included these routines, would just have the identical definitions, assuming I didn’t make any mistakes.)

You are saying you are passing a ‘data’ argument to PlotIconMethod and your IconGetter function isn’t receiving it? Well that’s weird. Are you sure? I will try ….
Yes just as you said.
Youre description of how it is supposed to work is totally in line with what Inside Macintosh says so I believe you and it. I must be flubbing something.
ill try your code directly soon and just be sure I get your results (just for good measure).
And i should spend more time passing simpler data as a param just to be sure I can see it at work.
Ill have to check tech note 306 to be sure we got the right addresses right for the function
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
The ‘data’ parameter definitely works for me. This code displays “foo” right next to the ResEdit icon (only changes are replacing NULL as last argument to PlotIconMethod with “\pfoo” and adding MoveTo and DrawString calls to my IconGetter).

Try starting from my code and modifying it to fit your use case … you must be making a small mistake somewhere.

1690115822898.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));
MoveTo(50,20);
DrawString((void *) data);
    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,(Ptr) "\pfoo")
    );
   
    while (!Button()) ;
}
 
  • Like
Reactions: MacOfAllTrades

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
… you must be making a small mistake somewhere.
Yes! Wow.. I found it. I was declaring (and defining) my IconGetter(...) function as:
Code:
Handle IconGetter(ResType iconType, Ptr data);
I was missing the word pascal. Correcting it to (as your code had it):
Code:
pascal Handle IconGetter(ResType iconType, Ptr data);

...fixed my problem! While debugging I noticed that the Ptr data address was not the address I was feeding in my call to PlotIconMethod(..., Ptr data). But moreover I (eventually) noticed that data's address WAS showing up but as the value for the parameter iconType. I was like wtf... So then I scrutinized the function header and realized I was missing that word pascal.

@Crutch -- Is the keyword pascal a compiler extension by the makers of ThinkC? From what a quick search yields, for compilers that support the keyword pascal or __pascal, at the least the parameters are reversed. This would jibe with what I witnessed with my two params being switched.
 

joevt

Tinkerer
Mar 5, 2023
73
32
18
All the C compilers for classic Mac OS support pascal because Mac OS apps was original programmed using Pascal. Look at all the Inside Macintosh books - they show APIs and code examples written in Pascal. pascal in C alters the parameter passing calling conventions to match those of Pascal - at least for code compiled for 68K. Maybe check out the "Mixed Mode Manager" in "Inside Macintosh: PowerPC System Software" for more information about that for 68K and also PowerPC.

I think the order in C is different to support variadic functions? Regardless of how many parameters you pass, the first parameter is always at the same location relative to the current stack frame pointer. But isn't Writeln a variadic procedure call in Pascal? Well, it's a special procedure with non-standard parsing for unlimited arguments and colon modifiers. I don't think you can make your own procedure with variable number of parameters.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
Yes that’s right. C functions pass arguments right-to-left to support variadic functions, require the caller to clean up the stack (since they don’t know how many arguments they got), and also return their result in register D0. Pascal functions pass arguments left-to-right, clean up the stack themselves, and return their result on the stack.

Any function in your code that will be used as a callback from the Macintosh ROM must be declared pascal. Failing to do so is an easy way to introduce strange behavior and random crashes, I do it all the time …
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
The ‘data’ parameter definitely works for me. This code displays “foo” right next to the ResEdit icon (only changes are replacing NULL as last argument to PlotIconMethod with “\pfoo” and adding MoveTo and DrawString calls to my IconGetter).

Try starting from my code and modifying it to fit your use case … you must be making a small mistake somewhere.

View attachment 12992

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));
MoveTo(50,20);
DrawString((void *) data);
    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,(Ptr) "\pfoo")
    );
  
    while (!Button()) ;
}
One thing I don't get about the above code is that if I write an app where I have to use this PlotIconMethod(....,IconGetter,...) function a lot, there is a memory leak. Because you call NewHandle(...) but never ReleaseHandle() or ReleaseResource().
I'm *not* criticizing your code, but rather and trying to think of how/ where I could ReleaseHandle() the handle h you create in IconGetter()?
It returns the handle because the PlotIconMethod()'s internals call IconGetter() and need it to return a handle--- but that means you can't ReleaseHandle() inside of IconGetter()... and PlotIconMethod is not a function we can modify, but it doesn't seem to ever Release the handle itself... so how do I get that handle back?

The only thing I can think of is if in that Ptr data [last param of PlotIconMethod] we pass it some variable that we can (within IconGetter()) save that handle address such that in main() after you cal PlotIconMethod(..) you do a ReleaseHandle().


I'm tempted to think there has to be an easier way but I'm no seeing one in InsideMacintosh or anywhere.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
I agree! This seems like a weird aspect of using PlotIconMethod().

I would either do what you suggest, or (presuming you are plotting one icon at a time) just have PlotIconMethod stuff the handle into a global variable and call DisposeHandle() every time. I don’t think there’s a better way.
 
  • Like
Reactions: MacOfAllTrades

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
I agree! This seems like a weird aspect of using PlotIconMethod().

I would either do what you suggest, or (presuming you are plotting one icon at a time) just have PlotIconMethod stuff the handle into a global variable and call DisposeHandle() every time. I don’t think there’s a better way.
Thanks for replying and confirming!!
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
Wow - as I was messing with all this and the memory leak and Releasing resources I managed to damage my Finder (and more?). To the point where the Finder's icon suite was messed up and so was its reported ProcessType. Very funky stuff. I'm guessing the memory I was releasing or writing to somehow got inadvertently pointed to the Finder's resource space or something.... anyway a fresh system install fixes it but amazing the power to d
The ‘data’ parameter definitely works for me. This code displays “foo” right next to the ResEdit icon (only changes are replacing NULL as last argument to PlotIconMethod with “\pfoo” and adding MoveTo and DrawString calls to my IconGetter).

Try starting from my code and modifying it to fit your use case … you must be making a small mistake somewhere.

View attachment 12992

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));
MoveTo(50,20);
DrawString((void *) data);
    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,(Ptr) "\pfoo")
    );
  
    while (!Button()) ;
}
@Crutch - Have you noticed that if you wanted to use your quoted program to plot the Finder icon (eg instead of ResEdit) - you could do:
Code:
pb.ioFileType    = 'FNDR';
pb.ioFileCreator = 'MACS';
The funny thing is -- this works if I'm in Black&white mode, but with screen set to colors, it quits on the following line:
Code:
Check(PBDTGetIconSync(&pb));
Reason being, PBDTGetIconSync(..) is failing and returning afpItemNotFound aka –5012.
Note this only happens with the FNDR/MACS. With any other example (eg ResEdit or whatever) it will properly show the black and white icon.

Wonder why..??
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
That’s weird. I have a half baked idea why that might happen (Finder doesn’t actually have a BNDL/ICN# in a normal sense but simply knows to displays a standard generic “system file” icon for itself … in a way, the question is almost more why does this even work in color for the Finder?) but you probably just need to treat the Finder as a special case.

Yeah you need to be very careful not to call ReleaseResource on any resources you might be grabbing, intentionally or not, from the System heap. It’s certainly plausible the Finder’s icon lives in the System heap and the Finder expects it to stay there, and if you nuke them, the Finder could later overwrite some of its own data structures when it tries to manage them itself. You are always fine calling DisposeHandle on a handle you allocated yourself with NewHandle, though.
 
  • Like
Reactions: MacOfAllTrades

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
That’s weird. I have a half baked idea why that might happen (Finder doesn’t actually have a BNDL/ICN# in a normal sense but simply knows to displays a standard generic “system file” icon for itself … in a way, the question is almost more why does this even work in color for the Finder?) but you probably just need to treat the Finder as a special case.

Yeah you need to be very careful not to call ReleaseResource on any resources you might be grabbing, intentionally or not, from the System heap. It’s certainly plausible the Finder’s icon lives in the System heap and the Finder expects it to stay there, and if you nuke them, the Finder could later overwrite some of its own data structures when it tries to manage them itself. You are always fine calling DisposeHandle on a handle you allocated yourself with NewHandle, though.
Yes. And I keep calling ReleaseResource(myHandle) instead of DisposeHandle which is probably my gateway to fudging up things. Perhaps I should be more surprised that I haven't fudged up the resources in any of my other applications I've run in my testing...

Thanks.
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
Also -- weirdest thing but your demo app (and my similar approach) ends up calling the IconGetter() function twice per PlotIconMethod(). Inside Macintosh says this should happen once per screen that intersects the rect you're working with -- I only have one screen so I'm a little confused by this.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
It runs once to get the mask, which is always in the ICN# resource. Then, unless you’re in b&w mode, it has to run a second time to get the actual icon data, which is (for example) in the icl8 resource.
 
  • Like
Reactions: MacOfAllTrades

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
Quick reference for those future readers:
@Crutch mentioned Tech Note #306. I found Apple still has these old Macintosh Technical Notes on their developer website. Here is Macintosh Technical Note #306.

I searched using this page but sadly I couldn't find it by searching "tech note #306" or anything like that. I could find it only by searching for the title of the Technical Note -- I found this PDF on an old ftp site that had a list of technical notes and their title and number (see pdf attachment).

Hope this helps future vintage coders!
 

Attachments

  • Macintosh Technical Notes list.pdf
    2.7 MB · Views: 124
  • Like
Reactions: Patrick

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
It runs once to get the mask, which is always in the ICN# resource. Then, unless you’re in b&w mode, it has to run a second time to get the actual icon data, which is (for example) in the icl8 resource.
I had some really bizarre issue --

Unless I call NewHandle(s) inside of IconGetter() function, the resulting plotted icon would have some artifacts (white streaks on the otherwise correct icon). I was trying to call NewHandle just once way outside in Main() knowing that I'll be using that block of memory over and over again... Long story short, I had to call NewHandle(s) inside of IconGetter, which means it's called once for the mask-getting call and again for the icon-data-getting call. But then I had to DisposeHandle() for each of these separately after I had returned from PlotIconMethod(). My solution was to provide PlotIconMethod(...) with a pointer to a custom data struct that had two Handle objects. handle1 and handle2 as well as a counter that kept track of which of those two has been allocated. Then, after returning from PlotIconMethod() I could examine my counter variable and see if one or both handles had been allocated memory during the call(s) to GetIcon(). Then I could call DisposeHandle() on one or both of those handles.

This solved my memory leak issue and somehow made plotting icons happy again.

I'm guessing the problem is that in those two calls to GetIcon (one for mask, one for pixel map) it's overwriting the memory in the handle so you get mask and pixexlmap overwriting the same memory. So unless these are two separate blocks of memory you get unexpected plotting artifacts. I'm also guessing that between the two calls to IconGetter() that are made in the depths of PlotIconMethod() (which I can't see of course I just know it's calling IconGetter twice...) that it :
- Calls IconGetter() to get the handle to the mask data
- saves the handle to the mask data
- calls IconGetter() to get the handle to the pixel map data
- saves the handle to the pixel map data
calls appropriate plotting functions and points to the mask data and pixel map data
So if the second call to IconGetter() overwrites the mask data then you'll get artifacts in the final plotted icon... which all make sense, but this just makes the overall process by which PlotIconMethod() works all the more convoluted because, if you want to manage memory without leaks, there needs to be two separate handles of memory but they have to be managed in creative ways by keeping track of them sort of indirectly.... Hard to put into words succinctly so my apologies if the above sort of doesn't make sense.

To put it into context of the simple demo program @Crutch provided -- imagine if you wanted to run PlotIconMethod() a lot (e.g. in a loop or once every time a key is pressed). Well you'd leak memory if you didn't Dispose of the handle that is being allocated memory within the function GetIcon(). But you can't call DisposeHandle() inside that GetIcon() function because the caller (PlotIconMethod()) isn't done working with he ram. And PlotIconMethod needs at most 2 handles of memory (one for mask, one for pixel map) but it gets them both using the same function (GetIcon()) so within GetIcon() you won't easily know whether this is a mask call, a pixel map call (or a one-off B&W icon call). So you'll need add'l logic to figure that out and communicate that info back to one of your calling functions so you know what/when to free later.

Another way to do it would be to just make two handles (global or pass it through the PlotIconMethod data pointer) and just allocate them ONCE outside of any loops and then add logic to the GetIcon() function so that it can return the correct handle (basically alternating between the two each time). This should work (though I haven't tried) and should accomplish my original goal which was to only allocate memory to the handle(s) ONCE in my program since I know the maximum memory it'll need (one mask handle worth and one large 8 bit icon worth)... I'll have to try this to check that it works as expected.. UPDATE:::: I tried this and yes this works, but note that for some reason the size of the memory allocated to the Handle you use to save the Mask data MUST be the correct size of kLargeIconSize. If I made it be kLarge8BitIconSize then for some reason the resulting icon is improperly masked.. -- So somehow the plotting functions are affected by the size of a handle even if it is simply larger than the size of memory they need???? Very confused by this and this message is long enough but I don't see how a handle (pointer to a pointer) block of memory of 1024 bytes that gets written with 128 bytes causes problems?

Anyway... it works now and without memory leaks so with that, I have color working in MacDock!!! :) Big thanks to @Crutch.

Screenshot 2023-08-18 at 10.19.45 AM.png
 
Last edited:

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
UPDATE:::: I tried this and yes this works, but note that for some reason the size of the memory allocated to the Handle you use to save the Mask data MUST be the correct size of kLargeIconSize. If I made it be kLarge8BitIconSize then for some reason the resulting icon is improperly masked.. -- So somehow the plotting functions are affected by the size of a handle even if it is simply larger than the size of memory they need???? Very confused by this and this message is long enough but I don't see how a handle (pointer to a pointer) block of memory of 1024 bytes that gets written with 128 bytes causes problems?
First of all, so glad this is working in color - congrats!

This issue sounded super weird so I couldn’t resist looking into it, and so checked out the source for for the Icon Utilities which you can find here. https://github.com/elliotnunn/super...1994-02-09/Toolbox/IconUtils/IconUtils.c#L823

Search that page for GetHandleSize and you’ll see this code:

C:
maskByteLength = myGetHandleSize(theMask);

if (maskByteLength < rowBytes)
    ...
else
    theBlock->maskMap.baseAddr = (*theMask) + (maskByteLength >> 1);

It’s not totally obvious from the fragment, but what the Icon Utilities manager is doing is taking the ICN# resource and just assuming that the mask data starts halfway through the byte buffer, as measured by the size of the handle that contains it.

So yeah sure enough, you’d better make sure the handle you use for the b&w ICN# (which includes the mask) is exactly the right size! Otherwise “halfway thru the memory block pointed to by the handle” won’t be the spot where the mask begins.

Nice find …
 
  • Wow
Reactions: MacOfAllTrades

MacOfAllTrades

Tinkerer
Oct 5, 2022
168
182
43
So yeah sure enough, you’d better make sure the handle you use for the b&w ICN# (which includes the mask) is exactly the right size! Otherwise “halfway thru the memory block pointed to by the handle” won’t be the spot where the mask begins.

Nice find …
Thanks for confirming! I had no idea someone had code like this out there.
And yes I hear you loud and clear on how the code assumes the buffer for the icon has its second half with the mask. This is quite interesting (funny?) because from what I've read in Inside Macintosh indeed the mask is supposed to be part of the B&W icon with the mask data coming in right after the icon bitmap. In the case where the handle is exactly the right size then yes, 'right after the b&w bitmap' does == '2nd half of the buffer' --- but without a specification of a requirement that the buffer be of the exact appropriate size, a developer should not be expected to use a buffer of the exact size... so a little shame cast on the early 90s / late 80s Mac OS team ;-). Well.., I'll let it slide for sure 🙃