• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

Is there a programming forum here for help using the Toolbox Manager and Quickdraw Manager?

matthoyt

Member
First, I'm programming on a Macintosh Classic II in System 7. I'm trying to draw a 32x32 bit icon in C using the StuffHex() function. I'm using the Think C reference guide and working in that IDE. I know you can do all this in ResEdit and then use PlotIcon() but I'd like to learn how to store the 128-byte bit image on the heap using NewHandle() and then use a pointer to that data to draw out the icon in a window. There is function called StuffHex() but it only takes a data structure object like a Pattern or a Cursor or a BitMap. Is there a forum of expert programmers that understand programming using the Inside Macintosh manuals and can help me out?

Thanks!
 

Snial

Well-known member
First, I'm programming on a Macintosh Classic II in System 7. I'm trying to draw a 32x32 bit icon in C using the StuffHex() function. I'm using the Think C reference guide and working in that IDE. I know you can do all this in ResEdit and then use PlotIcon() but I'd like to learn how to store the 128-byte bit image on the heap using NewHandle() and then use a pointer to that data to draw out the icon in a window. There is function called StuffHex() but it only takes a data structure object like a Pattern or a Cursor or a BitMap. Is there a forum of expert programmers that understand programming using the Inside Macintosh manuals and can help me out?

Thanks!
You need to start with Inside Macintosh, volume 1. Read at least up to the QuickDraw chapter.


The way I'd probably do it is to create a bitmap data structure and make it point to your bit image (with the right dimensions). Then you'll need to use CopyBits (which is a ToolBox API), the destination will be ScreenBits which will be on the current grafport. The part of the grafport where you want to draw needs to be invalidated, otherwise it'll all be clipped and you won't see anything. Also because you're using a heap pointer, you'll need to indirect it first to get the pointer, because bitmaps are pointers not handles.

If it's the case that you plan to use a fixed amount of memory for your icons or bitmap images, it's probably best to simply allocate them as a pointer at the start using NewPtr instead of NewHandle. If you're using a variable number of bitmap images that need to be allocated and deallocated, then NewHandle will work, but you'll probably have to HLock the handle when doing things like CopyBits to stop the OS from moving memory around.
 

cheesestraws

Well-known member
I'd like to learn how to store the 128-byte bit image on the heap using NewHandle() and then use a pointer to that data to draw out the icon in a window.

The very first thing you're going to have to get your head around here is what a handle is. It's not like other OSes where a handle is an integer just representing an object. A handle on the Mac is specifically a pointer to a pointer, and it's used so that there's a layer of indirection between the object you're asking for and where it is in memory. This allows the memory manager to do useful stuff like defragment the heap without you having to know or care that it's done it.

So when you have a handle to a heap block, you also already have a pointer to that heap block - with the caveat that you mustn't store just the pointer unless you've told the OS you're going to do that, because the OS will sometimes move your heap block around to make best use of memory.

So you'll find that a lot of OS and Toolbox APIs use handles when other OSes would use pointers, because a handle is basically a pointer with some added flexibility.

I would suggest personally that if you want to do things on this level for learning purposes, that you start with the built-in text I/O that Think C provides and try to get your head around the memory manager before also trying to learn the Toolbox. It won't provide as shiny results, but you can just use ordinary C text I/O and you're only learning one alien thing at once.

If on the other hand you're trying to learn to make Mac GUIs, you should note that the way you're trying to do it here is highly unidiomatic, and you should use resources - you'll do a lot of work here to no purpose if you want to build a whole UI this way.

Is there a forum of expert programmers that understand programming using the Inside Macintosh manuals and can help me out?

I'm not sure we'll be the resource you want, but a small handful of us hang out in the Hacks and Development forum and give help when we can!
 

matthoyt

Member
The very first thing you're going to have to get your head around here is what a handle is. It's not like other OSes where a handle is an integer just representing an object. A handle on the Mac is specifically a pointer to a pointer, and it's used so that there's a layer of indirection between the object you're asking for and where it is in memory. This allows the memory manager to do useful stuff like defragment the heap without you having to know or care that it's done it.

So when you have a handle to a heap block, you also already have a pointer to that heap block - with the caveat that you mustn't store just the pointer unless you've told the OS you're going to do that, because the OS will sometimes move your heap block around to make best use of memory.

So you'll find that a lot of OS and Toolbox APIs use handles when other OSes would use pointers, because a handle is basically a pointer with some added flexibility.

I would suggest personally that if you want to do things on this level for learning purposes, that you start with the built-in text I/O that Think C provides and try to get your head around the memory manager before also trying to learn the Toolbox. It won't provide as shiny results, but you can just use ordinary C text I/O and you're only learning one alien thing at once.

If on the other hand you're trying to learn to make Mac GUIs, you should note that the way you're trying to do it here is highly unidiomatic, and you should use resources - you'll do a lot of work here to no purpose if you want to build a whole UI this way.



I'm not sure we'll be the resource you want, but a small handful of us hang out in the Hacks and Development forum and give help when we can!
Thank you. And yes, I know you’re right. I’m doing this the hard way. But thought I would try to build an icon with hex and using the heap instead of building an icon in ResEdit and using something like PlotIcon(). I figured if I can do this than I really understand what a bit image actually is vs a bitmap and how to build any graphic just using hex.

I appreciate the toolbox is designed to make that process much more efficient and practical. That’s why they built those tools. I suppose it is a challenge I would like to see myself succeed in.

I have the Inside Macintosh Vol 1. Been reading a lot about handles. Strange the way they did this but I think I understand. Just need to know how to dereference appropriately.

Perhaps I’m biting off more than I can chew and may move over to text I/o to learn memory allocation and print functions first. I just like the idea of creating a graphic instead.

In any case, thank you. And I’ll look for that development forum. I was searching on the site and couldn’t find you guys. Thank you! I was hoping there was a wealth of knowledge somewhere on the web so I started here. I look forward to chatting more!!
 

Phipli

Well-known member
Thank you. And yes, I know you’re right. I’m doing this the hard way. But thought I would try to build an icon with hex and using the heap instead of building an icon in ResEdit and using something like PlotIcon(). I figured if I can do this than I really understand what a bit image actually is vs a bitmap and how to build any graphic just using hex.

I appreciate the toolbox is designed to make that process much more efficient and practical. That’s why they built those tools. I suppose it is a challenge I would like to see myself succeed in.

I have the Inside Macintosh Vol 1. Been reading a lot about handles. Strange the way they did this but I think I understand. Just need to know how to dereference appropriately.

Perhaps I’m biting off more than I can chew and may move over to text I/o to learn memory allocation and print functions first. I just like the idea of creating a graphic instead.

In any case, thank you. And I’ll look for that development forum. I was searching on the site and couldn’t find you guys. Thank you! I was hoping there was a wealth of knowledge somewhere on the web so I started here. I look forward to chatting more!!
A good starting point might be the book the Macintosh C Programming Primer Vol 1.
 

matthoyt

Member
You need to start with Inside Macintosh, volume 1. Read at least up to the QuickDraw chapter.


The way I'd probably do it is to create a bitmap data structure and make it point to your bit image (with the right dimensions). Then you'll need to use CopyBits (which is a ToolBox API), the destination will be ScreenBits which will be on the current grafport. The part of the grafport where you want to draw needs to be invalidated, otherwise it'll all be clipped and you won't see anything. Also because you're using a heap pointer, you'll need to indirect it first to get the pointer, because bitmaps are pointers not handles.

If it's the case that you plan to use a fixed amount of memory for your icons or bitmap images, it's probably best to simply allocate them as a pointer at the start using NewPtr instead of NewHandle. If you're using a variable number of bitmap images that need to be allocated and deallocated, then NewHandle will work, but you'll probably have to HLock the handle when doing things like CopyBits to stop the OS from moving memory around.
Hello Snial,

You are exactly where I am at. I do have the inside Macintosh vol I book. I’m reading under the QuickDraw section and the Toolbox utilities section. So if I either use NewPtr or NewHandle, once the memory is allocated for 128 bytes, the question is how do I store the hex data into that memory allocation using StuffHex. StuffHex requires a pointer to a data structure. Should I create a bitmap and set the beginning address of the bitmap to the pointer or dereference handle on the heap that I just created? And then use CopyBits? I do get the idea of locking the handle if I dereference it. I can see examples of that I. The Think C reference guide.
 

Snial

Well-known member
Hello Snial,

You are exactly where I am at. I do have the inside Macintosh vol I book. I’m reading under the QuickDraw section and the Toolbox utilities section. So if I either use NewPtr or NewHandle, once the memory is allocated for 128 bytes, the question is how do I store the hex data into that memory allocation using StuffHex. StuffHex requires a pointer to a data structure. Should I create a bitmap and set the beginning address of the bitmap to the pointer or dereference handle on the heap that I just created? And then use CopyBits? I do get the idea of locking the handle if I dereference it. I can see examples of that I. The Think C reference guide.
StuffHex just converts hex into bytes and dumps it into the pointer.

Let's say we just deal with pointers and not handles for the moment. The only real advantage with Handles is that you can move them in memory and still keep track of the data (and the OS will purge them if they're marked as purgable and it's running out of heap space). So, let's ignore that and just start with NewPtr. You have a Classic II, it can probably cope with allocating 128b in a fixed location ;-)

C:
typedef unsigned char uint8_t; // prob doesn't exist in THINK C from 1991
#define k32x32IconBytes 128
// RowBytes must be even.
#define WidthToRowBytes(aWidth) (((aWidth+15)>>4)*2)
#define kMyIconWidth 32
typedef uint8_t tMy32x32IconData[k32x32IconBytes];

BitMap *gMyIconBm;
tMy32x32IconData *gMy32x32IconData;

// Then the code itself in some Init procedure.
gMyIconBm=NewPtr(sizeof(BitMap)); // now we have a data structure for the bm.
gMy32x32IconData=NewPtr(sizeof(tMy32x32IconData)); // and some memory for the icon.
gMyIconBm->baseAddr=gMy32x32IconData;
gMyIconBm->rowBytes=WidthToRowBytes(kMyIconWidth);
// Right and Bottom are both 1+ the last coordinate.
SetRect(&gMyIconBm->bounds, 0, 0, 32, 32);

// Later you want to write the data.
// You'll have to do StuffHex in 2 parts, because Pascal strings can only be up to 255 bytes long.
StuffHex(gMy32x32IconData,"\pFirst64BytesInHex");
StuffHex(gMy32x32IconData+64,"\pSecond64BytesInHex "); // now it's written.
// It's actually pointers to the data structures.
CopyBits(&gMyIconBm, &myWindow->port.portBits, &gMyIconBm->bounds,
         /* dst bounds */, srcCopy, NULL);
 

cheesestraws

Well-known member
the question is how do I store the hex data into that memory allocation using StuffHex.

Why are you trying to use stuffhex here at all? If you want to do this, the way to do it would be to have a constant string literal, say, with your content in it (or a constant byte array with an array initialiser), then copy that into the heap block.

StuffHex requires a pointer to a data structure

StuffHex just takes the address of a block of memory. It's not tied to being any particular data structure. It's a string operation.

I’ve got that too. Unfortunately it doesn’t speak to creating an icon. It creates these graphics programs like Mondrian and others that don’t quite get to the subject I’m trying to resolve.

Please don't take this as a personal criticism, but I think you are currently trying to build an engine before you know how to drive a car. Start more simply, then go deeper.
 

matthoyt

Member
StuffHex just converts hex into bytes and dumps it into the pointer.

Let's say we just deal with pointers and not handles for the moment. The only real advantage with Handles is that you can move them in memory and still keep track of the data (and the OS will purge them if they're marked as purgable and it's running out of heap space). So, let's ignore that and just start with NewPtr. You have a Classic II, it can probably cope with allocating 128b in a fixed location ;-)

C:
typedef unsigned char uint8_t; // prob doesn't exist in THINK C from 1991
#define k32x32IconBytes 128
// RowBytes must be even.
#define WidthToRowBytes(aWidth) (((aWidth+15)>>4)*2)
#define kMyIconWidth 32
typedef uint8_t tMy32x32IconData[k32x32IconBytes];

BitMap *gMyIconBm;
tMy32x32IconData *gMy32x32IconData;

// Then the code itself in some Init procedure.
gMyIconBm=NewPtr(sizeof(BitMap)); // now we have a data structure for the bm.
gMy32x32IconData=NewPtr(sizeof(tMy32x32IconData)); // and some memory for the icon.
gMyIconBm->baseAddr=gMy32x32IconData;
gMyIconBm->rowBytes=WidthToRowBytes(kMyIconWidth);
// Right and Bottom are both 1+ the last coordinate.
SetRect(&gMyIconBm->bounds, 0, 0, 32, 32);

// Later you want to write the data.
// You'll have to do StuffHex in 2 parts, because Pascal strings can only be up to 255 bytes long.
StuffHex(gMy32x32IconData,"\pFirst64BytesInHex");
StuffHex(gMy32x32IconData+64,"\pSecond64BytesInHex "); // now it's written.
// It's actually pointers to the data structures.
CopyBits(&gMyIconBm, &myWindow->port.portBits, &gMyIconBm->bounds,
         /* dst bounds */, srcCopy, NULL);
Haha!! You are a steely eyed missle man!! I'm at work at the moment so can't implement. But this is exactly the help I was looking for. I'm so glad you pointed me to NewPtr as replacement function for NewHandle. The Think C reference instructs me to use NewHandle and then to lock the handle. I'm going to try it this weekend. If it works, you get all the credit!! I'll show you the result. Thank you!! I think the other member is right. I'm trying to rebuild an engine before I know how to drive, but I really wanted to figure this out.
 

matthoyt

Member
StuffHex just converts hex into bytes and dumps it into the pointer.

Let's say we just deal with pointers and not handles for the moment. The only real advantage with Handles is that you can move them in memory and still keep track of the data (and the OS will purge them if they're marked as purgable and it's running out of heap space). So, let's ignore that and just start with NewPtr. You have a Classic II, it can probably cope with allocating 128b in a fixed location ;-)

C:
typedef unsigned char uint8_t; // prob doesn't exist in THINK C from 1991
#define k32x32IconBytes 128
// RowBytes must be even.
#define WidthToRowBytes(aWidth) (((aWidth+15)>>4)*2)
#define kMyIconWidth 32
typedef uint8_t tMy32x32IconData[k32x32IconBytes];

BitMap *gMyIconBm;
tMy32x32IconData *gMy32x32IconData;

// Then the code itself in some Init procedure.
gMyIconBm=NewPtr(sizeof(BitMap)); // now we have a data structure for the bm.
gMy32x32IconData=NewPtr(sizeof(tMy32x32IconData)); // and some memory for the icon.
gMyIconBm->baseAddr=gMy32x32IconData;
gMyIconBm->rowBytes=WidthToRowBytes(kMyIconWidth);
// Right and Bottom are both 1+ the last coordinate.
SetRect(&gMyIconBm->bounds, 0, 0, 32, 32);

// Later you want to write the data.
// You'll have to do StuffHex in 2 parts, because Pascal strings can only be up to 255 bytes long.
StuffHex(gMy32x32IconData,"\pFirst64BytesInHex");
StuffHex(gMy32x32IconData+64,"\pSecond64BytesInHex "); // now it's written.
// It's actually pointers to the data structures.
CopyBits(&gMyIconBm, &myWindow->port.portBits, &gMyIconBm->bounds,
         /* dst bounds */, srcCopy, NULL);
I did it!! Let me show you the code that finally worked. Built from your code, but with some slight differences. I want to explain what I learned in the process. I'm new to this. How do I drop the code in this reply so it doesn't look like crap....like you did it?

1705179812236.png
 

Snial

Well-known member
I did it!! Let me show you the code that finally worked. Built from your code, but with some slight differences. I want to explain what I learned in the process. I'm new to this. How do I drop the code in this reply so it doesn't look like crap....like you did it?

View attachment 68060
Great stuff! Well done! I see you took the 32x32 and copied it to a much larger bitmap, so QuickDraw enlarged it for you :) ! However, slightly curious, you said you were programming a Classic II, but that's only monochrome; at a standard 512x342 resolution, and this is in colour. So, I guess you did this on e.g. infinitemac or another emulator?

Also, @Phipli & @matthoyt , have you seen the attachment number? I believe it's 68060 and 68061, how cool is that?

-cheers from Julz
 

matthoyt

Member
So here's the code...see my comments. Would love to get your thoughts....

C:
#define kBaseResID    128
#define kMoveToFront (WindowPtr)-1L
#define kIconBytes    128

typedef unsigned char uint8_t;
typedef uint8_t IconData[kIconBytes];

char    *gMyIconDataPtr;    // NewPtr() returns a char ptr not a IconData ptr...more below
BitMap     myIconMap;   // initialized a BitMap on the stack...would love to know how to create
                        // a BitMap struct on the heap
WindowPtr    window;
Rect destRect;


/*  Functions */

void ToolBoxInit(void);
void WindowInit(void);
void IconInit(void);


/* main */

void     main(void) {
    
    ToolBoxInit();
    WindowInit();
    IconInit();
    
    
    while( !Button() );

}

/*  ToolBoxInit */

void    ToolBoxInit(void) {
    
    InitGraf(&thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(nil);
    InitCursor();

}

/*  WindowInit  */

void    WindowInit(void) {

    
    window = GetNewWindow(kBaseResID, nil, kMoveToFront);
    
    if(window == nil) {
    
        SysBeep(10);
        ExitToShell();
    
    }
    
    ShowWindow(window);
    SetPort(window);
    
    SetRect(&destRect, 40, 20, 200, 180);  //set up the Rect within the window
                                        // set it to 5 multiples of 32
    

}

/* IconInit    */

void    IconInit(void) {

    gMyIconDataPtr = NewPtr(sizeof(IconData));  //this worked great! But again, returns a ptr
                                                // to a char, not a ptr to type IconData
                                                // NewPtr appears to be like malloc() but always
                                                // returns a pointer to a char
    
    myIconMap.baseAddr = gMyIconDataPtr;    // set BitMap's baseAddr to gMyIconDataPtr
    myIconMap.rowBytes = 4;                 // simplified your code to just '4' based on a Think C                                            // reference
    SetRect(&myIconMap.bounds, 0, 0, 31, 31);   // set the bounds Rect to size of bit image
    
    StuffHex(gMyIconDataPtr, "\p003FE000007FF000007FFC0000C78E000180070001000380020001800200008002000080020000800200008002F83C800200028002783C80025AAC8002008080020080800200408002044080020240800207E180031FF38001B01F0001C8270000C7CE0000FBFC00007C3A0000BFFD0007BFF5C018CFC3382080010440800102");
    
    
 //the only thing I'm not sure I understand below is the 2nd argument to CopyBits().  
//thePort is a apparently a global variable and the Think C reference showed this as 
//an example and it worked.  However, your code to use the window->port.portBits 
//made more sense to me, but I kept getting a compile error 'No member value' 
//or something like that.  Any explanation to that?
    
    CopyBits(&myIconMap, &thePort->portBits, &myIconMap.bounds, &destRect, srcCopy, nil);

}
 

matthoyt

Member
Great stuff! Well done! I see you took the 32x32 and copied it to a much larger bitmap, so QuickDraw enlarged it for you :) ! However, slightly curious, you said you were programming a Classic II, but that's only monochrome; at a standard 512x342 resolution, and this is in colour. So, I guess you did this on e.g. infinitemac or another emulator?

Also, @Phipli & @matthoyt , have you seen the attachment number? I believe it's 68060 and 68061, how cool is that?

-cheers from Julz
I did all the coding on my Macintosh Classic II in beautiful monochrome. However, I wanted to get it over to you so you could see it. So had to attach the harddrive to a Basilisk II emulator on my MacBook so I could copy/paste the code. But it compiled and ran first on my compact Mac. What a rush!! It worked!!
 

joevt

Well-known member
CopyBits gets interesting when you get into Color QuickDraw and later Carbon.

The parameter is a BitMapPtr, but you can typecast a PixMapPtr or whatever and it will figure out what it is you passed to it by looking at the rowBytes. A PixMap has a rowBytes at the same offset as rowBytes from a BitMap. If the two most significant bits of rowBytes is 0b10 then it's a PixMap. If the two most significant bits are 0b00 then it's a BitMap.

GrafPort.portBits is a BitMap so you can pass an address to that.

thePort is a QuickTime global that you change with SetPort. In that case, you need to make sure the port has been set which you did in WindowInit.

You could pass a pointer to portPixMap of a CGrafPort. portVersion of the CGrafPort is at the same offset as rowBytes of BitMap. It has the two most significant bits set (0b11). If you typecast a CGrafPort to a GrafPort and pass portBits to CopyBits, then that works too because portBits has the same offset as portPixMap.

window is a WindowPtr. A WindowPtr is a GrafPtr so you could just pass &window->portBits to CopyBits.

To get the WindowRecord, you can typecast window to a WindowPeek then you can do window->port.portBits but you don't want to do that unless you want to look at fields that are specific to a WindowRecord.

In Carbon, there's GetPortBitMapForCopyBits
https://developer.apple.com/library/archive/qa/qd/qd61.html#//apple_ref/doc/uid/DTS10001914

Since the two most significant bits are flag bits, that means rowBytes can only be up to 16K - 1. 24-bit color pixels uses 4 bytes, so that's a max of 4095 pixels.
 
Top