Adapting MicroAnimationDemo to

Think Pascal 4.5


This is a little demonstration of how to adapt Pascal code made for Think Pascal 4.0.2 to Think Pascal 4.5d4 with my modified interfaces.

OK, start by opening MicroAnimationDemo with Think Pascal 4.5d4. If TP is not open or has no project file open, you can just drag-and-drop the project to the application. The project file opens, and the source should auto-open too. If it doesn't, double-click on "MicroAnimationDemo.p" in the project window.

This is the source-code before changes:

program MicroAnimationDemo;
uses
{$IFC UNDEFINED THINK_PASCAL}
Types, QuickDraw, Fonts, Events, Packages, Menus, Dialogs, Windows,{}
OSUtils, ToolUtils, Resources, Icons, TextEdit,
{$ELSEC}
{$SETC GENERATINGPOWERPC := false}
{$ENDC}
QDOffScreen;
const
kMaxObject = 5;
var
cicn: array[0..kMaxObject] of CIconHandle;
pos: array[0..kMaxObject] of Rect;
speed: array[0..kMaxObject] of Point;
wind: WindowPtr;
offScreen: GrafPtr;
backPat: PixPatHandle;
i: Integer;
startTicks: Longint;
 
(* Standard inits *)
procedure InitToolbox;
begin
{$IFC UNDEFINED THINK_PASCAL}
InitGraf(@qd.thePort);
InitFonts;
FlushEvents(everyEvent, 0);
InitWindows;
InitMenus;
TEInit;
InitDialogs(nil);
InitCursor;
{$ENDC}
end; (*InitToolbox*)
 
begin
InitToolbox;
 
{$IFC NOT GENERATINGPOWERPC}
{Is Color QD and 32-bit QD around?}
if NGetTrapAddress($A89F, ToolTrap) = NGetTrapAddress($AA1E, ToolTrap) then {Color QD?}
halt;
if NGetTrapAddress($A89F, ToolTrap) = NGetTrapAddress($AB1D, ToolTrap) then {32-bit QD?}
halt;
{$ENDC}
 
{Seed the random number generator}
{$IFC UNDEFINED THINK_PASCAL}
qd.randSeed := TickCount;
{$ELSEC}
randSeed := TickCount;
{$ENDC}
 
{Create window from resource}
wind := GetNewCWindow(128, nil, WindowPtr(-1));
SetPort(wind);
 
{Make GWorld with default depth and CLUT}
{$IFC UNDEFINED THINK_PASCAL}
if noErr <> NewGWorld(GWorldPtr(offScreen), 0, wind^.portRect, nil, nil, pixelsLocked) then
{$ELSEC}
if noErr <> NewGWorld(GWorldPtr(offScreen), 0, wind^.portRect, nil, nil, [pixelsLocked]) then
{$ENDC}
halt;
{We lock the offscreen pixmap so we can CopyBits and PlotCIcon to it.}
if LockPixels(CGrafPtr(offScreen)^.portPixMap) then
;
 
SetGWorld(GWorldPtr(offScreen), nil);
 
{Get a pattern and set it to the background pattern for offScreen}
backPat := GetPixPat(128);
BackPixPat(backPat);
 
{Set up all objects, loading the 'cicn' resource, and setting its position and speed}
for i := 0 to kMaxObject do
begin
cicn[i] := GetCIcon(128 + i);
pos[i] := cicn[i]^^.iconMask.bounds;
OffsetRect(pos[i], abs(Random) mod wind^.portRect.right, abs(Random) mod wind^.portRect.bottom);
speed[i].h := Random mod 8;
speed[i].v := Random mod 8;
end;
 
{*** The animation loop: ***}
 
while not Button do
begin
startTicks := TickCount;
{Erase the offscreen, resetting it to the background image/pattern}
SetGWorld(GWorldPtr(offScreen), nil);
EraseRect(wind^.portRect);
{For all objects, move, draw off-screen and do border checks}
for i := 0 to kMaxObject do
begin
OffSetRect(pos[i], speed[i].h, speed[i].v);
PlotCIcon(pos[i], cicn[i]);
if pos[i].top < 0 then
speed[i].v := abs(speed[i].v);
if pos[i].left < 0 then
speed[i].h := abs(speed[i].h);
if pos[i].bottom > wind^.portRect.bottom then
speed[i].v := -abs(speed[i].v);
if pos[i].right > wind^.portRect.right then
speed[i].h := -abs(speed[i].h);
end;
{Copy from offscreen to screen}
SetGWorld(GWorldPtr(wind), GetMainDevice);
CopyBits(offScreen^.portBits, wind^.portBits, wind^.portRect, wind^.portRect, srcCopy, nil);
{Wait until at least one tick has passed}
while TickCount = startTicks do
;
end;
{Set back the device before we quit}
SetGWorld(GWorldPtr(wind), GetMainDevice);
end. {MicroAnimationDemo}

Note the "{$IFC UNDEFINED THINK_PASCAL}" lines. They show where TP 4.0.2 and CodeWarrior don't agree on the rules. For example, CW require a lot of units in the "uses" clause that Think auto-uses.

Let's do the work!

Hit cmd-B to build it. (Cmd-R is OK too, if you want to run after building.) Oh, QDOffScren requires Types.p? So, let's add it. Select "Add file..." from the Project menu, and add "Types.p" from the "Interfaces" folder. Then, drag it so that it is above QDInterfaces.p in the project folder, to show that it should be compiled before QDOffScreen.p.

Hit cmd-B again. Types.p and QDOffScreen.p will compile. With MicroAnimationDemo, you will get an error inthe "uses" clause. This is caused by the new QDOffScreen.p, which depends on some declarations in Types.p. Move "Types" from inside the conditional compilation part to outside, like this:

uses
Types,
{$IFC UNDEFINED THINK_PASCAL}
QuickDraw, Fonts, Events, Packages, Menus, Dialogs, Windows,{}
OSUtils, ToolUtils, Resources, Icons, TextEdit,
{$ELSEC}
{$SETC GENERATINGPOWERPC := false}
{$ENDC}
QDOffScreen;

OK, so hit cmd-B again. It says "backPat" is already declared at this level. Well, BackPat is a function, and due to a problem with 4.5d4, I had to declare it in Types.p. So, we have to replace all backPat with something else, say theBackPat.

The next problem is more interesting, and with a pleasant result:

{$IFC UNDEFINED THINK_PASCAL}
if noErr <> NewGWorld(GWorldPtr(offScreen), 0, wind^.portRect, nil, nil, pixelsLocked) then
{$ELSEC}
if noErr <> NewGWorld(GWorldPtr(offScreen), 0, wind^.portRect, nil, nil, [pixelsLocked]) then
{$ENDC}

This code complains on incompatibility between actual and formal value parameter, an error that usually means that you send the wrong type to a function. And, we are. In the old days, the last parameter was a Pascal-style set. However, since C can't handle sets at all and that made C users unhappy, they changed it to an integer. This change is what causes the $IFC above. But, with the new interfaces, the difference is gone! Just remove all the conditional stuff and keep the upper version! All you keep is

if noErr <> NewGWorld(GWorldPtr(offScreen), 0, wind^.portRect, nil, nil, pixelsLocked) then

Hit cmd-B once more. Hey... no errors! Run... and it works! And the code is even a bit simpler than before, with fewer differences to CodeWarrior!

One more change before we leave! We can remove one more conditional thing:

{Seed the random number generator}
{$IFC UNDEFINED THINK_PASCAL}
qd.randSeed := TickCount;
{$ELSEC}
randSeed := TickCount;
{$ENDC}

With the new interfaces, the global "qd" is equivalent to using the QuickDraw globals directly. To conform with the new standards, I recommend that you use the "qd." prefix. So, change the code to:

{Seed the random number generator}
qd.randSeed := TickCount;

And it works! The only conditional code that is three blocks in the beginning. First, there are the conditional "uses". Second, Think auto-initializes the Toolbox, so we shouldn't do that. Third, I check for Color QuickDraw, but only when running on a 68k Mac, since Powermacs always have it. Checking rather than assuming is always a good idea! If you will only work in Think, you can clean it up, and get rid off all the conditional "junk".

For adapting other demos to Think Pascal 4.5d4, you will mostly make the same changes.

Go back to the Think Pascal 4.5d4 page.


Copyright ©1998 Ingemar Ragnemalm.

Updated: 8-April-1998