ThinkC [Study Group 2] - Events & Menu Management

Relating to ThinkC Development

pfuentes69

Active Tinkerer
Oct 27, 2021
380
293
63
Switzerland
it takes ~47 seconds to render on the 040 IIci.
Look, I see this a decent figure... considering that rendering this on Basilisk in my MacBook Air i7 takes about 5 seconds.

I have a copy of this book, sitting in a ledger (now in a box, sadly, after several movings) since I finished the University (more than 25 years ago) as I wanted to make a raytracing engine, but even if it's unbearably slow compared to modern standards I enjoy making these things work in an old platform... it's more rewarding to me than using a modern framework.
 

KennyPowers

Active Tinkerer
Jun 27, 2022
278
315
63
Look, I see this a decent figure... considering that rendering this on Basilisk in my MacBook Air i7 takes about 5 seconds.

I have a copy of this book, sitting in a ledger (now in a box, sadly, after several movings) since I finished the University (more than 25 years ago) as I wanted to make a raytracing engine, but even if it's unbearably slow compared to modern standards I enjoy making these things work in an old platform... it's more rewarding to me than using a modern framework.
Ya, and like I said...I have made zero optimization passes over any of that code...it was just "get something that looks correct on the screen" :)
 
  • Like
Reactions: pfuentes69

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
It's too bad parallelism isn't trivial on this platform, as this is an embarrassingly parallel problem. Running a similar algorithm using 32 threads on my 5950X renders the same scene in the blink of an eye :)
Can you explain what you mean here? Unless you are calling WaitNextEvent in your rendering loop (which you hopefully aren’t), you are getting 100% of the CPU (aside from interrupt servicing …. Which you could turn off if you really wanted to 🤪 ). How would parallelism make this run any faster?
 

KennyPowers

Active Tinkerer
Jun 27, 2022
278
315
63
Can you explain what you mean here? Unless you are calling WaitNextEvent in your rendering loop (which you hopefully aren’t), you are getting 100% of the CPU (aside from interrupt servicing …. Which you could turn off if you really wanted to 🤪 ). How would parallelism make this run any faster?
Sure. At a high level, the way this works is it shoots a ray from the "eye" through every pixel in the view port. Each of those rays bounces around and builds up a color for that pixel. That means that there aren't any dependencies between pixels, or anything that would prevent shooting all of those rays simultaneously if you had enough cores or threads to do it (rendering a pixel just reads the scene data...doesn't modify it). So on my 16-core, 32-thread 5950X for example, I get a near linear speedup by divvying up the pixels evenly across 32 threads (well, linear up to 16 threads, and then still an appreciable speedup from 16 to 32...SMT). I very specifically said "parallelism" (truly being able to do multiple tasks at the same time), not "concurrency" (having more than one thing in-flight at the same time).
 
Last edited:

Kabootie Computey!

New Tinkerer
Sep 13, 2022
15
19
3
www.kabootie.com
Good news, Multi Pong fans! I just pushed a new version to GitHub (ß3) which is much more challenging! In this version, the ball won't bounce off a surface that's occluded by another window (thanks to this tip from @Crutch). It will also keep moving during mouseDown events (specifically, it will calculate its correct location after the mouse button is released), so now you can't pause the game by holding the mouse button down. I also slowed down the ball to balance out the difficulty a bit. I dare say, it's a little bit fun. Try it out!

I might do one more update before calling this final... I'd like to get rid of the flicker, and maybe let the player select a difficulty level. In the meantime, enjoy! Thanks to everyone for the feedback and tips. I'm really enjoying these study groups.
 

pfuentes69

Active Tinkerer
Oct 27, 2021
380
293
63
Switzerland
Good news, Multi Pong fans! I just pushed a new version to GitHub (ß3) which is much more challenging! In this version, the ball won't bounce off a surface that's occluded by another window (thanks to this tip from @Crutch). It will also keep moving during mouseDown events (specifically, it will calculate its correct location after the mouse button is released), so now you can't pause the game by holding the mouse button down. I also slowed down the ball to balance out the difficulty a bit. I dare say, it's a little bit fun. Try it out!

I might do one more update before calling this final... I'd like to get rid of the flicker, and maybe let the player select a difficulty level. In the meantime, enjoy! Thanks to everyone for the feedback and tips. I'm really enjoying these study groups.
Hey, this is quite cool and fun!

I just see that the ball keeps changing the position while moving other windows, but actually it will disappear while this happens and appear again when you release the button... is this the intended behaviour?
 
  • Like
Reactions: Kabootie Computey!

Kabootie Computey!

New Tinkerer
Sep 13, 2022
15
19
3
www.kabootie.com
Hey, this is quite cool and fun!

I just see that the ball keeps changing the position while moving other windows, but actually it will disappear while this happens and appear again when you release the button... is this the intended behaviour?
Thanks :)

And yeah, I don't think I can do any game or window content updates while DragWindow() is happening, so the best I could figure is to make the ball invisible while the window is being moved, then let the game loop re-draw it afterwards in the right spot.


Although now that you mention this and I read the code, I just realized the game re-draws the ball in its original location for one frame and then jumps it to the right spot on the next tick. That's a bit awkward. I just tried fixing that, and it feels a smidge more natural. I'm going to push a new version with this fix now...

Edit: new version pushed - https://github.com/jcgraybill/multipong/releases/download/1.0-beta.3/Multi.Pong.b3a.sit.hqx
Edit edit: newer version, adding five selectable difficulty options! https://github.com/jcgraybill/multipong/releases/download/1.0-beta.4/Multi.Pong.b4.sit.hqx
 
Last edited:

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
I don't think I can do any game or window content updates while DragWindow() is happening

You should actually be able to do this by writing a function taking no parameters and sticking a pointer to it in the global variable DragHook. I’ve never done this, but it’s documented in Inside Macintosh: Volume I. Try it!

C:
#include <LoMem.h>

void MyDragHook()
{
    if (TickCount() > ...)
        // update game state ....
}

...
DragHook = (ProcPtr) MyDragHook;
 
  • Wow
Reactions: Kabootie Computey!

Kabootie Computey!

New Tinkerer
Sep 13, 2022
15
19
3
www.kabootie.com
You should actually be able to do this by writing a function taking no parameters and sticking a pointer to it in the global variable DragHook. I’ve never done this, but it’s documented in Inside Macintosh: Volume I. Try it!

Thanks for this suggestion! I tried it, and it works pretty well - I've got it in a branch in Github here, and I'll stick a build in this post. There are some visual artifacts I want to figure out. Sometimes the ball doesn't get erased from a window that's being moved, so it leaves a ghost ball behind. Also if the dashed box that follows the mouse around during the drag bumps into the ball, it also leaves a little bit of visual stuff behind.

I made a micro-app that demonstrates this in use outside the context of the game. Also attaching a build here, in case anybody is excited to watch a ball bounce around in a window. Maybe you have a cat?

C:
#define    kBallSize    20

Rect gBall = { 100, 100, 100 + kBallSize, 100 + kBallSize };
short gHorizontal = 8;
short gVertical = 10;
WindowPtr gWindow;

void Step( void ) {
    EraseOval(&gBall);
    
    gBall.left += gHorizontal;
    gBall.top  += gVertical;
    gBall.right += gHorizontal;
    gBall.bottom += gVertical;
    
    if ( gBall.left < 0 || gBall.right > gWindow->portRect.right ) gHorizontal *= -1;
    if ( gBall.top < 0  || gBall.bottom > gWindow->portRect.bottom ) gVertical *= -1;
    
    PaintOval(&gBall);
}

void MyDragHook( void ) {
    GrafPtr oldPort;
    GetPort( &oldPort );
    SetPort( gWindow );
    Step();
    SetPort( oldPort );
}

void main() {
    Rect WinBounds = { 50, 30, 200, 200 };
    short quit = false;
    
    InitGraf( &thePort );
    InitFonts();
    FlushEvents( everyEvent, 0 );
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(0);
    InitCursor();

    gWindow = NewWindow( 0L, &WinBounds, "\pClose me to exit", true, documentProc, (WindowPtr)-1L, true, 0);
    if ( !gWindow ) ExitToShell();
    SetPort( gWindow );
    EraseRect( &gWindow->portRect );
    ForeColor(blackColor);

    DragHook = (ProcPtr) MyDragHook;
    
    while ( !quit ) {
        EventRecord event;
        WindowPtr EventWin;
        short ThePart;
        
        if ( WaitNextEvent( everyEvent, &event, 1L, nil ) ) {
            ThePart = FindWindow( event.where, &EventWin );
            switch ( event.what ) {
                case mouseDown:
                    switch ( ThePart ) {
                        case inDrag:
                            DragWindow( EventWin, event.where, &screenBits.bounds );
                            break;
                        case inGoAway:
                            if (TrackGoAway(EventWin, event.where)) quit = true;
                            break;
                    }
                    break;
                case updateEvt:
                    BeginUpdate( (WindowPtr)event.message );
                    // noop
                    EndUpdate( (WindowPtr)event.message );
                    break;
            }
        } else {
            Step();
        }
    }
}
 

Attachments

  • Draggy Pong.sit.hqx
    6.5 KB · Views: 66
  • DragHook.sit.hqx
    1.6 KB · Views: 72
  • Like
Reactions: pfuentes69

Kabootie Computey!

New Tinkerer
Sep 13, 2022
15
19
3
www.kabootie.com
How do you use GitHub with this code?
I tried but because the encoding of the new-line is different in GitHub the Mac files didn’t appear properly
I ran into the same problem. Stuffit Deluxe has a set of utilities in its "Translate" menu that are a big help. I'm using Stuffit Delux 3.5.1 to BinHex my builds, and to create copies of the .c files with UNIX or Windows line feeds that Github understands. Once I do that, I transfer them to my modern computer the old fashioned way...

IMG_8327.jpeg
 
  • Like
Reactions: pfuentes69

pfuentes69

Active Tinkerer
Oct 27, 2021
380
293
63
Switzerland
I ran into the same problem. Stuffit Deluxe has a set of utilities in its "Translate" menu that are a big help. I'm using Stuffit Delux 3.5.1 to BinHex my builds, and to create copies of the .c files with UNIX or Windows line feeds that Github understands. Once I do that, I transfer them to my modern computer the old fashioned way...

View attachment 9564
Cool. Good to know!

I also have to give a new try to Amendhub.com
 

pretzelFTW

New Tinkerer
Sep 5, 2022
26
8
3
Anyone still around? With the holidays and a few other projects, I've been away for awhile but I'm back now and would like to finish this.
 

KennyPowers

Active Tinkerer
Jun 27, 2022
278
315
63
Anyone still around? With the holidays and a few other projects, I've been away for awhile but I'm back now and would like to finish this.
I'm here...got a bit distracted from this though. I liked the structure of these "study groups" though...made me find the time.
 

pretzelFTW

New Tinkerer
Sep 5, 2022
26
8
3
Some interesting behaviour in the EventTracker code. It generally works, but I'm noticing that the window scrolling doesn't happen for the suspend and resume events, so when I draw a string in those cases, it's drawing over the previous event.

Screenshot 2023-03-02 at 9.05.08 PM.png


Looking at the DrawEventString function:

C:
void DrawEventString( Str255 eventString ) {
    RgnHandle tempRgn;
    WindowPtr window;
    window= FrontWindow();
    tempRgn = NewRgn();
    ScrollRect( &window->portRect, kHorizontalOffset, -kRowHeight, tempRgn );
    DisposeRgn(tempRgn );
    MoveTo( kLeftMargin, kRowStart );
    DrawString( eventString );
}

I suspected the problem might be with that call to FrontWindow(). Perhaps with the app being suspended, it's not getting a window or the correct window from that call. I tried storing the window in global variable after creating it and then using that instead of calling FrontWindow().

C:
void DrawEventString( Str255 eventString ) {
    RgnHandle tempRgn;
    WindowPtr window;
    window= gMainWindow; // global variable I assigned to in WindowInit()
    tempRgn = NewRgn();
    ScrollRect( &window->portRect, kHorizontalOffset, -kRowHeight, tempRgn );
    DisposeRgn(tempRgn );
    MoveTo( kLeftMargin, kRowStart );
    DrawString( eventString );
}

That appears to fix the problem. Two things I don't understand though:
  1. Why doesn't it work as originally written? Presumably it worked for the author when he wrote this. Maybe an OS difference? (I'm on 7.5.3)
  2. If FrontWindow() is returning the wrong thing, then why does the drawing of the string work a few lines later?
 

pretzelFTW

New Tinkerer
Sep 5, 2022
26
8
3
If FrontWindow() is returning the wrong thing, then why does the drawing of the string work a few lines later?
I guess because DrawString() doesn't take a port argument and it's just using the port of the last thing I was drawing to – which was the same window.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
That code breaks because FrontWindow() is getting a bad result — possibly NULL? — while the app is suspended. (FrontWindow() can only ever return a pointer to a window in the foreground application.). The code is using FrontWindow() solely to get a portRect to scroll, so a bad result from FrontWindow() prevents the scrolling, but you are correct that this doesn’t prevent DrawString() front continuing to draw to the current port.

This would probably work without the global variable (which is however a good solution) if you just remove the call to FrontWindow() entirely and just scroll your current port instead:

ScrollRect( &thePort->portRect, kHorizontalOffset, -kRowHeight, tempRgn );

(Depending on your version of the Mac headers, you may need ’qd.thePort’ there instead.)

The mystery to me is why FrontWindow() is getting called at all at a time when your app isn’t in the foreground, which should only be e.g. if you get a null event after you get suspended. In “set project type” did you check any of the options like “get background null events” (I forget what it’s called….)
 
  • Like
Reactions: pretzelFTW

pretzelFTW

New Tinkerer
Sep 5, 2022
26
8
3
In “set project type” did you check any of the options like “get background null events” (I forget what it’s called….)
Screenshot 2023-03-03 at 10.10.25 AM.png


Hmm… not seeing anything quite like that.

I tried to inspect the value of the window pointer in the debugger. I'm always seeing the correct value, but I'm also never seeing the scrolling issue while running that way, so most likely the debugger is interfering somehow.

I'm going to poke at this some more this evening – just out of curiosity :)