Drawing Things from Scratch in Carbon - Part 1

(using LIDE and C)

© 2002 Lightsoft

Rev 1.0 - 17/05/02

Rob Probin

 

Introduction

Basically this article details how to set up a window and draw some things on that window; we do all of this using the Carbon calls. I discuss some of the problems and take a look at setting up a project in depth.

I'm also planning not to supply any pre-built projects for this article - the idea is you can set it up as you go through.

Tools you need

Let's go though what we need, first.

I am using:

1. LIDE 3

2. MPW with MrC 5.0d2c2. Any version of MrC around this should do fine.

3. Carbon Headers and Libraries. Get these from Apple, and install into the folders "Libraries" and "Interfaces" inside the "MPW" folder (inside the "LIDE3" folder.)

4. ResEdit 2.1.3. A free resource editor from Apple. It hasn't been updated in a while, but it is good enough for our purposes.

4. (Optionally) Access to Apple's Developer Site

You will also need:

1. A PowerPC computer running at least Mac OS 8.1. It is better to have Mac OS 8.6, Mac OS 9 or Mac OS X (preferably 10.1 or later) however. I have not tested this code on Mac OS 8.1, but I have avoided putting anything in which will not work (at least to my knowledge) where not explicitly noted in the text.

Just so that everyone knows what everything is,

LIDE - Lightsoft's integrated development environment. This refers to the whole suite of Lightsoft tools.

Anvil - The core tool of LIDE, which does the editing and contains the project manager. This is where we will be spending most of our time in this project.

Fantasm - Lightsoft's Advanced Macro assembler for PowerPC and 68K code. We are not using this here, but we could to produce a PowerPC assembly version of this document. I may do this at a later date.

MPW - A suite of tools provided by Apple for a long time to do Macintosh development. The C compiler with this suite, MrC, is very good, and we use it with Anvil.

 

Setting up Anvil

The first thing to do after booting Anvil is to check that the preferences are set up to make it easier for us. We will come onto this in a moment.

You'll also notice that an Application called Toolserver has been booted automatically by Anvil. Anvil uses this to communicate with the C compiler MrC. Anvil has it's own plug-in tool architecture, and uses an Anvil tool called the "MrC AIT", which accepts commands from Anvil for building, and dispatches them to Toolserver over Apple Events. Tool server then translates these to build commands for MrC.

Now onto the preferences...

General preferences. This item is under the EDIT menu. The preferences are set up as several panes of information; this is what the Show: drop down list does - select different pieces of information to edit. These are the ones I would set.

Speed Options

OFF - Build Hogs CPU - Since MrC is physically separate (loaded into ToolServer), the last thing we want is Anvil hogging the processor. Even when running Fantasm (i.e. doing assembly language programming) it is usually much better not to have this turned on.

Save Options

ON - Save every few minutes - just in case something terrible happens, or you forget to save for several days (don't laugh, I've been there).

ON - Save files on switch - this means that when you go to do something, like say run your application, all changed files are saved. This avoids you losing everything when the Application crashes [Like Pagemill just did to me dear reader - Ed]. Very important in Mac OS 7, 8 and 9. In my case (since I'm using Mac OS X and building a carbon application that runs natively in MacOS X protected memory) it is less important - but it is still worth it I feel.

Default Language

When working with C, I find it is easier to get Anvil to do a lot of the file language assignment for me. By selecting ANSI C as the language, and by clicking the two check boxes in this preference pane you can assign a language automatically to files that are added to the project, or just opened for editing. Of course, this does not suit all situations but it's pretty good.

New And Update File Options

ON - Save State Information With File - allows you to start working straight away when you pick up where you left off in a project since the cursor will be at the location in the file you last edited.

 

Tool & Plugs Preferences. These are under the Edit menu also. We are interested in one specific tool - MrC Interface Tool. The options I have selected are:-

ON - Require Prototypes

OFF- Strict ANSI

ON - Warnings On

 

 

A Little About Carbon

For those that don't know, Carbon is a cleaned up version of the Traditional Mac OS API (application programmers interface) primarily for Mac OS X. It also runs with Mac OS 8.1* and onwards.

 

There were several problems with the Classic Mac OS programming interface when Apple were considering it for use with Mac OS X. One of these (that we will come across) was to do with the Window Ports and Graf Ports that windows are made out of. Not only could the internal data structure be accessed, but the types could be cast between the two. Mac OS X, with double buffered windows, protected memory and a fully independent window manager really can't allow this sort of thing to happen.

Another thing was that a lot of the operating system calls would need to be re-implemented on the new operating system base and there are/were thousands, a lot of which are old and unused. Hence the drive to go to a redefined, but very similar, API.

As a side issue, porting a pre-existing application to Carbon isn't as simple as it might at first appear. But writing a new program isn't particularly any different, except for one or two changed API commands.

 

Apart from slightly different OS calls, programs need to link against CarbonLib rather than Interfacelib (the old Macintosh API). These libraries contain the system code that gets executed when a program calls a system routine - NewCWindow for example.

To use Carbon you need CarbonLib installed on your machine. With more and more programs being both Mac OS 8/9 as well as Mac OS X, most people have carbon lib on their systems. Certainly it has been standard part of the operating system from Mac OS 9.

*The latest released version at the time of writing, CarbonLib 1.5 (although 1.6 isn't far away) and supports Mac OS 8.6. and greater. All versions since CarbonLib 1.2 have required Mac OS 8.6 and later. CarbonLib 1.0.4 is the latest version that supports Mac OS 8.1. But, of course, some of the calls are only available from the newer CarbonLibs.

 

Setting Up The Project Using Anvil

Go to the Project menu and select Create New Project...

This will bring up the New Project dialog box. At the top center of the box is a drop down list that allows the specification of the general project configuration.

Select MrC Project, then in the Output State type "drawing_things" (without the quotes) instead of "MrC_project".

Finally we must set the Flags in the SIZE resource. The SIZE resource tells the operating system some basic information about the application and what it needs to do when launching the application.

One the left hand side of the dialog is a Drop down list box that currently reads "Build Process Tools". Change that to "SIZE Resource & Misc Flags". You will see a set of checkboxes. These are the ones that you should tick (or leave if they are already ticked):-

Accept suspend events

Can background

Does activate on FG switch

32 Bit compatible

High level event aware

Uses TextEdit services

 

Now click the OK button.

This will bring up a save dialog, and ask you to save the project. So select an appropriate folder (maybe create one?), and save the project file.

You will now have a project file in your selected folder.

 

Our First File

Now time for our first source file. Select New from the File menu. Now paste the text below into the window (and you may need to do some formatting so it looks this same). Once you've done that click on Save in the Edit menu. I called my file "drawing_things.c".

// test program

#define TARGET_API_MAC_CARBON 1

#include <quickdraw.h>
#include <Events.h>

// Function Prototypes
void main(void);

// Main - Program entry point
void main(void)
{
// new variables go here

// initialize some basic stuff
FlushEvents(everyEvent, 0); // get rid of any pre-existing events
InitCursor(); // Change cursor from wristwatch to arrow

// new code goes here

}


Getting Building

We need to add our file, drawing things.c, to the project file. To do that we need to click the little floppy disk icon on the drawing_things Project Window (second item along from the left). Navigate and click on the "drawing_things.c" file. This should put it into the high_level section of the project window.

If it does not, then you may need to click on the file, and select ANSI C as the default language. Then drag the file icon in the window to the correct position (drawing_things_high_level).

Now click build! (the little Anvil icon - fourth icon from left to right on the project window).

#PPCLink> drawing_things.c.4 Reference to unresolved symbol ".FlushEvents"

 

This is telling us is couldn't link with the system call "FlushEvents". This is not surprising as we have not told it where to find it!

Now, MrC uses StubLibraries (unlike Fantasm that uses the actual shared libraries themselves). StubLibraries contain no code, only the names of functions - sort of like the contents of a book without any other text.

The system stub libraries, and in particular CarbonLib, are located within LIDE3 : MPW : Libraries : SharedLibraries folder.

To add CarbonLib click the Disk Icon (second one along on the project window). Select "All Documents" in the Show section of the navigation window. Now navigate to LIDE 3 folder, and inside this is the MPW folder, and inside this is the Libraries folder ,and inside this is the SharedLibraries folder, and inside this is CarbonLib. Click on CarbonLib and click the ADD button at the bottom of the window.

Now click build! (or presses Apple-B)

In the log window you should see:-

Build finished.
No errors.

That's great. You can now hit run (The little Target icon or pressApple-R)

 

The program will start and then instantly quit - because we haven't told it to do anything yet.

 

Making a window

Now we need to add two bits of code to make a window. Firstly we need need some variables, so on the next line after the comment "new variables go here" add:

WindowPtr my_window; // a window pointer
Rect my_rect; // rectangle size storage

And next we need the code to draw a window - add this on the next line after "new code goes here"

SetRect(&my_rect,50,50,400,400);

my_window=NewCWindow(0,&my_rect,"\ptitle",1,0,(WindowPtr)-1,0,0);
if(my_window==0) { return; }

SetRect sets up the size we want for our window, and NewCWindow actually generates the window. We check that a window really has been created before continuing, otherwise we quit. Windows may have problems being created, for example, if there is not enough memory present.

 

Now Build again (Apple-B)

drawing_things.c Line =22 #Error: function 'NewCWindow' has no prototype.

 

This means that the compiler cannot find the standard definition of NewCWindow - so lets add the system header that contains it.

On the line below the other two #includes add:

#include <windows.h>

Now Build again (Apple-B)

 

Making The Program Wait for Us

If you run (Apple-R), you may or may not see a window appear and disappear. The problem is that the program quits before we have a chance to see it.

So let's put a wait for mouse button piece of code right at the end of the function before the close braces:-

// wait for a mouse down event to come in
do {
WaitNextEvent(everyEvent, &theEvent, 0xffffffff, 0);
} while(theEvent.what!=mouseDown);

We also need to add a new variable (in the "add new variable here" section):-

EventRecord theEvent; // receive the event in here

You may want to look at Apple's documentation for WaitNextEvent. The loop is waiting for a mouseDown event to come in. We could have used Button() function, however we wanted to give all the spare time back to the system (and in Classic allow co-operative multitasking) and so WaitNextEvent has been told to wait for a very long time (0xffffffff clock ticks*) before returning to us unless there is an event for our application; in this case WaitNextEvent returns straight back to us. This means that all other applications get lots of processor time and we only get notified when there is something for us.

Again build the application, using Apple-B (or the menu item Build, or the icon on the project window).

Now run the application using Apple-R (or the menu item Run, or the icon on the project window).

 

You should see a blank window with the title 'title'. You will need to click your mouse to quit the program.

*A clock 'tick' is roughly 1/60th of a second -- 1/60.15 seconds I believe.

 

Making it run natively in Mac OS X

You will have probably noticed if you are running Mac OS X, that the application is running as a Classic application, complete with old style menu bar, and classic Platinum windows. [Phew, for a fleeting moment then I thought you were going to try get an 'Old Skool' reference in. It's only a matter of time dear reader - Ed]

However, if your application crashes it may take down Anvil and any other applications you are running under Classic. This is a bit of a pain. So now we are going to make our application launch as a native OS X carbon application.

We need to add something called a property list resource, also known as a 'plst' resource. This tells MacOS X that our single-binary Carbon CFM application is in fact a true Mac OS X application. You can add quite a bit of information inside plst resources, but we are just going to add a simple plst resource. (As a side note you can also create a resource called a 'carb' resource, which does much the same thing, but cannot contain additional information. Carb resources are considered the "old" way of doing it). [Close! - Ed]

If you are developing on Mac OS 8 or 9, then having a plst resource doesn't do anything to the application. However, if you cannot test the application in MacOS X it may not run at all. Having no plst resource means that it will most likely run fine under Classic. Of course most people like to run native applications under Mac OS X.

The choice is yours, of course, but I would suggest that if you cannot test it under X, you should not release it as an Mac OS X application - because it probably won't run properly. Although Carbon was designed as a cross platform application environment, the fact is that the two implementations of Carbon are slightly different, and not all of the calls and data accessors that are allowed in Carbon in MacOS 8 and 9 are allowed in Mac OS X. The same applies in the opposite direction, of course. It is easy to do something that runs on one, but not the other.

So how do we do it?

Open ResEdit, and open the application created by Anvil - mine's called "drawing_things".

Click on Create New Resource within the Resource Menu. Now in the text edit box type plst (that's all lower case) and then click the OK button. It will open a few windows. Since we do not want to add any data to the plst simply close the top window. You will notice it has created a plst resource, of ID 128, and it is 0 bytes in size. That's fine, just it's presence will cause Mac OS X to behave differently.

You can close the other two windows and save the file when it asks. We're done with ResEdit now so you can quit the program if you like.

If you run the application again (either from Anvil with Apple-R, or directly from the desktop) those of you with Mac OS X should see a nice new Aqua window.

For more information on building Mac OS X applications to go to this page on our site or Apple's developer pages.

 

Actually Drawing Stuff

Now is the time to add stuff to the window. Yipee! [It's taken me two hours to edit this far, I kid you not dear reader and now I'm faced with 'Yippee'? - Ed]

Add this code just before the section that waits for the mouse button to be pressed, but after the line that checks of the window pointer (my_window) ...

 

SetPortWindowPort(my_window);

MoveTo(100,320);
DrawString("\pClick mouse to finish");

Try to build (Apple-B) and run (Apple-R) it.

 

How about drawing some lines? Put these after the DrawString line.

 

MoveTo(10,10);
LineTo(30,30);
LineTo(50,10);
LineTo(70,30);
LineTo(90,10);
LineTo(110,30);

And in Part 2

That's it for part 1 - short and sweet (hopefully).

In Part 2 we will do a few new things, such as center the window, change the colours, and accept some keys. I'll also cover a few of the things to watch for.

 

As always if you have questions, please email me - rob@lightsoft.co.uk

 

 

Regards

Rob Probin, Lightsoft