ThinkC [Study Group 2] - Events & Menu Management

Relating to ThinkC Development

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
PlotIcon is what you need, confusingly in the Toolbox Utilities chapter. Or of course you can always stuff your icon into a bitmap and use CopyBits.

Edit: of course you aren’t actually getting an ICON resource this is just showing you how to draw it. I don’t remember if PBDTGetIcon() gives you back a pointer to a bitmap already — I think it does — in that case you can just pass that pointer to CopyBits. If not, it sounds like you know where the underlying icon bitmap data is, and you can just use a pointer to that data as the b.baseAddr below and plot it with CopyBits anyway.

C:
BitMap b = {NULL, 4, {0, 0, 32, 32} };
const Handle icon = GetResource(‘ICON’, 128);

HLock(icon);
b.baseAddr = *icon;
CopyBits(&b, &thePort->portBits, &b.bounds, &b.bounds, srcCopy, NULL);
For future readers - PBDTGetIcon updates your supplied PBDTRecord’s ioDTBuffer data with the icon data(first the icon bits and then the mask bits), not a bit map directly. So yes to your final point that you can just set a BitMap’s baseAddr field to point to the first address of your bits (in my case captured as a Str256 to hold both 32x32 icon and then mask data).
just wanted to add that for any future readers. Great help! Thanks again.

I’ll add, and this is no surprise, that the biggest benefit of this little self exercise is that I’ve been forced to use Inside Macintosh and in doing so learned how to navigate it as well as gotten a good feel for the paradigm apple used to supply data inputs and outputs to their functions. This is foundational learning that transforms the programmer from “poking around someone else’s house” into truly taking ownership where one’s skill can be applied.
Good stuff!
 
Last edited:
  • Like
Reactions: Crutch and eric

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
To the group: How long does my .c file have to be to where I should expect that phenomenon of the executable code being split across multiple chunks of memory or something..? And regardless, what does one have to do to account for that?

Asking because I've had my whole program in a single .c file but I notice if I call a function from some parts of the file it works but if I move this order or that ... like things start to break randomly. Using ThinkC's debugger I can see when it implodes and when it does the debugger says it's at an improper address or something and can't show you the code.
 

Crutch

Tinkerer
Jul 10, 2022
292
226
43
Chicago
The only time you should need to split your code into multiple segments is if your total complied code + string/data exceeds 32K. But the compiler will always tell you when that happens. Simply adding more C code to a file should never cause a successful-but-buggy compilation. I’m pretty sure you have something else that’s gone wrong in your code.

It sounds like you are ending up with the PC at a bad address. Usually that means you overwrote a return address on the stack with junk. Usually that means you wrote oversized data into a pointer to a local variable on the stack. An easy way to do that is to write past the bounds of an array declared locally.
 
  • Like
Reactions: MacOfAllTrades

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
The only time you should need to split your code into multiple segments is if your total complied code + string/data exceeds 32K. But the compiler will always tell you when that happens. Simply adding more C code to a file should never cause a successful-but-buggy compilation. I’m pretty sure you have something else that’s gone wrong in your code.

It sounds like you are ending up with the PC at a bad address. Usually that means you overwrote a return address on the stack with junk. Usually that means you wrote oversized data into a pointer to a local variable on the stack. An easy way to do that is to write past the bounds of an array declared locally.
Thanks, Crutch, for the info.
So I've nailed down the symptom a little better: I have my code split across two files. One bigger function in one fille and all the rest in the first file.
When i consolidate them into one file it works no problem If i keep them split it crashes often.
 

Crutch

Tinkerer
Jul 10, 2022
292
226
43
Chicago
That’s weird. There is no natural reason for that to happen. Is there any chance you have accidentally declared separate but identically-named globals in each file, such that moving the function causes it to use the wrong (perhaps uninitialized) global for some purpose?

If you use Macsbug you can probably tell where it is crashing. Make sure Macsbug names are turned on in your project options, then when it crashes use the ‘SC’ command or, if that doesn’t work, ‘SC7’.
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
Thanks for the suggestions. I'll play around with it all. Could be one of those things that doesn't make sense till it does.

Also - how does ThinkC debugger handle multiple files. I see no way to force it to open other .c files unless I actually step->into a function that resides in another file.
 

Crutch

Tinkerer
Jul 10, 2022
292
226
43
Chicago
I think that’s right. Or of course you can just add a breakpoint in one of those files by calling the Debugger() trap from any function therein.
 
  • Like
Reactions: Zane Kaminski

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
Ah here was my debugging problem:
Screen Shot 2023-04-20 at 10.20.06 PM.png


In any event - I think I'm past my last issue.

I spent a few hours tonight trying to find out how to construct an apple event that will launch or switch to another application.

I thought using the kCoreEventClass, kAEOpenApplication and providing the corresponding application signature would do it but alas no luck. This just sends the apple event to that app but it doesn't switch to it, bring it to focus, much less launch it. I'm **betting** I have to send an apple event to the Process Manager but I can't find an Inside Macintosh section on apple events the process manager can understand.. but I see the constants for it lingering around like the
typeAppParameters event descriptor.

I'll keep looking -it's gotta be close by somewhere, but any help is appreciated! :)
 

joevt

Tinkerer
Mar 5, 2023
41
26
18
Ah here was my debugging problem:
View attachment 11945

In any event - I think I'm past my last issue.

I spent a few hours tonight trying to find out how to construct an apple event that will launch or switch to another application.

I thought using the kCoreEventClass, kAEOpenApplication and providing the corresponding application signature would do it but alas no luck. This just sends the apple event to that app but it doesn't switch to it, bring it to focus, much less launch it. I'm **betting** I have to send an apple event to the Process Manager but I can't find an Inside Macintosh section on apple events the process manager can understand.. but I see the constants for it lingering around like the
typeAppParameters event descriptor.

I'll keep looking -it's gotta be close by somewhere, but any help is appreciated! :)

LaunchApplication takes a LaunchParamBlockRec where you can include some Apple Events.

You could also try sending Apple Events to the Finder. An existing process can be specified in an Apple Event descriptor using typeProcessSerialNumber. Find the Finder process serial number by searching for its type 'FNDR' and creator 'MACS' in the process list. Then create the Apple event using kAEFinderEvents, kAEOpenSelection. Maybe add a parent directory alias as a parameter, and a list of file aliases as another parameter. Then send the event.

C:
// actually Pascal without error checks
FindAProcess( kFinderSig, kSystemType, process );
AECreateDesc( typeProcessSerialNumber, @process, SizeOf( process ), myAddressDesc );
AECreateAppleEvent( kAEFinderEvents, kAEOpenSelection, myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID, aeEvent );
AEPutParamDesc( aeEvent, keyDirectObject, aeDirDesc );
AEPutParamDesc( aeEvent, keySelection, fileList );
AESend( aeEvent, ignorReply, kAENoReply+kAEAlwaysInteract+kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout, NIL, NIL );
 
  • Like
Reactions: MacOfAllTrades

Crutch

Tinkerer
Jul 10, 2022
292
226
43
Chicago
Why are you trying to do this with Apple Events?

To launch an application, call LaunchApplication. To switch to one that’s already running, call SetFrontProcess. You don’t need to use any Apple Events.
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
LaunchApplication takes a LaunchParamBlockRec where you can include some Apple Events.

You could also try sending Apple Events to the Finder. An existing process can be specified in an Apple Event descriptor using typeProcessSerialNumber. Find the Finder process serial number by searching for its type 'FNDR' and creator 'MACS' in the process list. Then create the Apple event using kAEFinderEvents, kAEOpenSelection. Maybe add a parent directory alias as a parameter, and a list of file aliases as another parameter. Then send the event.

C:
// actually Pascal without error checks
FindAProcess( kFinderSig, kSystemType, process );
AECreateDesc( typeProcessSerialNumber, @process, SizeOf( process ), myAddressDesc );
AECreateAppleEvent( kAEFinderEvents, kAEOpenSelection, myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID, aeEvent );
AEPutParamDesc( aeEvent, keyDirectObject, aeDirDesc );
AEPutParamDesc( aeEvent, keySelection, fileList );
AESend( aeEvent, ignorReply, kAENoReply+kAEAlwaysInteract+kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout, NIL, NIL );
Thanks that should be lots with me to work with. Ill mess with it this weekend and post results.
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
Why are you trying to do this with Apple Events?

To launch an application, call LaunchApplication. To switch to one that’s already running, call SetFrontProcess. You don’t need to use any Apple Events.
Why are you trying to do this with Apple Events?

To launch an application, call LaunchApplication. To switch to one that’s already running, call SetFrontProcess. You don’t need to use any Apple Events.
No need to do it with apple events. With my limited knowledge thats just what i thought was the way to do it not having seen anything about LaunchApplication in my learnings. If it isnt in the first dew chapters of the programming book this study group is following along with then theres a low chance I’ve seen it (though I am putting in the effort to look through the tomes of Inside Macintosh!)
 

Crutch

Tinkerer
Jul 10, 2022
292
226
43
Chicago
Oh okay! You should read the process manager chapter of Inside Macintosh Volume VI.

You don’t need to (and should not) send an Apple Event to the Finder to launch or switch to a process. The process manager has routines to do that for you!
 
  • Like
Reactions: MacOfAllTrades

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
Got through some of the Files and Process Manager sections of Inside Macintosh. I now have app switching working :)
See the video -- thanks all for the continued help.
Yes - it's a demo - so there are plenty of missing pieces that the video doesn't expose such as:
  • redrawing unhidden parts of the window (currently needs to rerun its app polling scheme to redraw)
  • automatic updating of app list when an app quits or launches (currently a keystroke :-( to do it)
  • make the Dock float above all windows all the time
  • plus many actual features..
But I'm proud and thankful nonetheless. PS Sorry to show this recording in an emulator.. I was working on it on my modern Mac today for a change (have been doing it all prior on my SE/30).

 

Crutch

Tinkerer
Jul 10, 2022
292
226
43
Chicago
This is beyond awesome, well done!

Probably updating the app list on a timer is the best approach. It should work fine. Otherwise you have to do wonky things like patch Launch and ExitToShell from an INIT, which might actually be impossible in the case of ExitToShell.

The floating window will probably be the trickiest bit.

By the way, my philosophy is that using an emulator to build things is 100% acceptable. It's just way faster and easier, plus the whole point is to have fun ....
 

MacOfAllTrades

Tinkerer
Oct 5, 2022
159
166
43
This is beyond awesome, well done!

Probably updating the app list on a timer is the best approach. It should work fine. Otherwise you have to do wonky things like patch Launch and ExitToShell from an INIT, which might actually be impossible in the case of ExitToShell.

The floating window will probably be the trickiest bit.

By the way, my philosophy is that using an emulator to build things is 100% acceptable. It's just way faster and easier, plus the whole point is to have fun ....
🙏 for the kind words and all the help.
regarding keeping the app list up to date I just tried hooking the update function to the NullEvent handler and it works great. I dont know how terrible this is for processor performance tho!
 

joevt

Tinkerer
Mar 5, 2023
41
26
18
🙏 for the kind words and all the help.
regarding keeping the app list up to date I just tried hooking the update function to the NullEvent handler and it works great. I dont know how terrible this is for processor performance tho!
Add a counter. Count the NullEvents per second. Compare this count where you do the update for every null event with the count where you do not do any update for any null event. A large difference means the update function is doing a lot of work.

Consider not doing the update function if less than a fraction of a second has passed since the last time you did the update function.
 

Crutch

Tinkerer
Jul 10, 2022
292
226
43
Chicago
Agree, an even easier approach would be to call WaitNextEvent with a sleep time of at least that minimum desired time interval. (About half a second seems reasonable to me.)

The Process Manager won’t give you a null event more often than your sleep interval calls for. And of course it can’t (because this is System 7!) preemptively give you time when someone else is using the processor. So you should be good.
 
  • Wow
Reactions: MacOfAllTrades

retr01

Senior Tinkerer
Jun 6, 2022
2,469
1
778
113
Utah, USA
retr01.com
And add a function to make the dock appear when the mouse touches a particular area on the bottom of the screen? After Dark and other CDEVs have that function, such as hot corners. :)
 
  • Love
Reactions: MacOfAllTrades

Crutch

Tinkerer
Jul 10, 2022
292
226
43
Chicago
Nice idea, probably need a VBL task for that to check mouse location while in the background then call SetFrontProcess (which, happily, you can do from a VBL task to bring your app to the foreground).