TweetFollow Us on Twitter

Apr 02 Palm OS

Volume Number: 18 (2002)
Issue Number: 04
Column Tag: Handheld Technologies

TicTacPalm

by Danny Swarzman

Getting started with Palm OS

Preface

Developing a Palm OS application comes naturally to a Mac programmer. You find familiar notions like resources, handle-based memory and an event loop. You can use CodeWarrior for Palm OS as the development environment.

This article presents a simple Palm OS application, TicTacPalm, which illustrates the main features of a Palm OS application. This application simply plays tic tac toe against the user.

The Palm OS Environment

The Palm Device

The Palm Operating System, Palm OS, is used in a growing number of products made by different manufacturers including Palm, Handspring and Sony.

The screen is typically divided into a 160x160 pixel display area, although at least one new device has a slightly longer screen. (Remember when Inside Macintosh admonished developers not to make assumptions about a fixed screen size? That advice holds true for Palm OS as well.) An application can detect stylus activity in this area. The stylus can also be used to enter characters in Palm's custom cursive writing system, graffiti, in the area just below the display. All stylus taps enter the system event queue as discrete pen events, similar to the mouse events you are familiar with in Mac OS. The graffiti input comes in the form of graffiti events.

Applications are loaded into the device by the synchronizing with a desktop computer through a serial port or IR communication. This is the standard way for a Palm device user to install your program. Some types of programs may be downloaded via a network connection, but we won't consider that approach here.


Figure 1. A typical Palm device. The Application Launcher program provides the equivalent of a "desktop" on a Palm Device.

Launching and Running Applications

The device shown in Figure 1 is displaying the equivalent of a desktop. Tapping an icon brings an application to the front. From the user's point of view, all applications are always running. When an application is brought to the front, it looks exactly like it did the last time it was in front.

An application must give up its temporary memory and close down each time another application comes to the front. The closing application can store information about the partially completed document or transaction prior to closing. Later, when the application is activated again it can read the stored information and restore the program state. A special database is used for this purpose, the preferences. There are functions in the API designed specifically for reading and writing preferences, greatly simplifying the task.

Forms

Like a window on a desktop system, a Palm OS Form object occupies an area of the screen. Due to the small screen sizes on current devices, many forms use the entire visible area. An application may have several forms and switch between them in response to user actions. At any given time, one form is in front and interacting with the user.

In Palm OS parlance, a window is any entity in which drawing occurs. Both forms and offscreen bitmaps are windows. Offscreen bitmaps allow you to create a composite image and then transfer it flicker-free to the screen.

Using Constructor, the GUI design tool included with CodeWarrior for Palm OS, you can create forms and populate them with controls. Controls include buttons, checkboxes, lists, etc. Each control is identified by a symbol or name that you specify. Each name will be included in the auto-generated resource file along with a numeric equate. For example, suppose you have a button named New on the form named GameBoard. In the generated header file, Constructor inserts a #define for the symbol GameBoardNewButton. Its value is the numeric ID of the button. Using the symbol (and not the numeric value) in your source code makes the code easier to read and understand.

Controls and Gadgets

Controls include interface elements such as buttons, labels, fields, etc. For most controls, there is a standard behavior provided by the OS. The exception is the gadget. The application defines a gadget's behavior, responding when the stylus enters its area and drawing its contents. Most of the time you can use the pre-built controls, and use gadgets only when custom appearance or behavior is required.

Memory

Palm OS uses a handle-based memory management system. These handles can't be treated as pointers to pointers, as is done in Mac OS. Instead, to reference data a program first locks a handle. The handle-locking function returns a pointer to the data. You can then use the pointer to transfer data to or from the area. Remember to unlock the handle when you are done using it, or heap fragmentation may result. This becomes even more of an issue on older devices with less memory. If you are aiming for compatibility with older devices and/or older versions of Palm OS, take a close look at the available dynamic heap space versus your program's requirements. The issue exists in a less severe form on modern devices and versions of the operating system.

Events

The operating system maintains a queue of events. Whenever the system requires the services of an application that has been launched, it sends events to the application.

For many events there is a standard response. When the user presses the stylus on the menu bar, the sequence of stylus events is interpreted as a menu command. The menu command then becomes a new event that is placed in the queue.

Some events will appear in your application's event handler, but are really intended for the system. You can ensure that your application hands events to the appropriate Palm OS functions by retaining the AppEventLoop exactly as provided in the Starter.cpp , part of the template code that is generated when you build a new project.

Everything relating to your application from launch to "shutdown" will arrive in the form of events. For example, at launch time an application-defined function, called from the main event loop, processes a request to load a form. That function assigns a callback function to the form to assist in the handling of events occurring in controls in that form. Once the form is loaded the callback function handles subsequent events.

A gadget, the custom user interface object discussed earlier, can have a callback function too. The application assigns a callback to each gadget when the form is opened. This is how a gadget responds to system requests to draw itself, and to handle taps in the gadget.

Developing for Palm OS

CodeWarrior and Alternatives

The vast majority of applications are developed using CodeWarrior However, the official Palm SDK, available on the Palm web site, works with many development environments. Other tools that are available to create Palm applications include a gnu compiler and a translator from Java bytecode to assembler for the handheld.

The sample code in this article was developed using CodeWarrior for Palm OS, Version 7. It comes with everything you need to develop for Palm OS 3.5, including the Palm SDK and related documentation. Current devices run Palm OS 3.5 and Palm OS 4.0, although with proper attention to detail you can build an application supporting earlier versions of the operating system.

The CodeWarrior package includes two disks - one to develop on the Mac, one to develop on Windows. The sample code accompanying this article was developed on the Mac.

Creating a Project

Use the New command from the File menu. In the dialog that appears, enter a name for the new project and choose Palm OS 3.5 Stationary.


Figure 2. Creating the project in CodeWarrior.

A second dialog will appear, giving you a few choices for the template project. For the sample code, Palm OS C++ App was used. CodeWarrior then generates a ‘Starter' project. This project produces a do-nothing application to use as a framework on which to build. You need to rename some things if your project's name isn't to be ‘Starter'. I just replaced every ‘Starter' occurrence with ‘TicTacPalm' in the code, file names, etc.

Constructor

Constructor for Palm is an interactive resource editor included with CodeWarrior for Palm. Constructor creates a collection of resources that contribute to the appearance of the application, including menus, forms and icons. In addition to editing a resource file, Constructor generates a header file that defines symbols for all the resources in the resource file.


Figure 3. The Constructor project window.

Debugging, Simulator and POSE

A Palm application can run in one of these environments:

  • the Palm Operating System Emulator (POSE)
  • the Simulator
  • the device itself

The sample project, like the Starter project has two targets: TicTacPalm and TicTacPalmSim.

TicTacPalm generates a Palm OS application that can be run in POSE or on an actual device. CodeWarrior provides a Palm OS debugger to step through the object code for this target.

For the TicTacSim target, a collection of simulator libraries is included in the project. When the project is built, a MacOS application is created. CodeWarrior's MacOS debugger can be used to step through this application. This is the easiest way to debug. There are limitations to this approach in that you don't have access to other applications or permanent data, which is not significant for our game project, but may be an issue for more complex applications.

There are a couple of strange rituals that you need to follow when using the simulator. The first time you run a program with the simulator, a dialog asks you to find the resources file. You must locate :CW for Palm OS Platform 7.0:Metrowerks CodeWarrrior:Palm OS Support:Simulator:SystemResources. You may notice that the simulator window sticks to the menu bar on your screen. To fix that, remove the Palm OS Simulator Preference file from the System Folder's Preferences subfolder. You only need to do these things once or twice when you are first using the product.

Most of the time, POSE is the preferred debugging platform. POSE is a desktop application that emulates the entire Palm OS device. It allows you to switch between applications, and even retain information between runs. You set up POSE to emulate a particular Palm OS device by copying the ROM from your device or by getting the ROM file for another device from the Palm web site. The POSE application must be running when you run the TicTacPalm target. Use the CodeWarrior debugger for Palm OS with this target.

Debugging while running on the device itself is possible but less convenient. It wasn't needed for the sample application. When the code works in simulation, load the .prc file onto the device as you would any Palm application.

User's View of TicTacPalm

Forms

TicTacPalm includes these forms:

  • Game Board
  • Game Info
  • About

Most of the action occurs on the game board (see Figure 4). The user taps on a square to make an ‘X' move. The program responds by playing an ‘O'. There are two buttons: New causes the Game Info dialog to be displayed, while Clear erases all the plays.


Figure 4. The game board.

The game information form has one field for the user to enter a name for the game. There is an OK button and a Cancel button. OK starts a new game with the name in the name field. Cancel returns to the game board without altering the name or the position.


Figure 5. Game info form.

The About form is a modal dialog that appears in response to the About menu command.


Figure 6. The About form.

Inside TicTacPalm

Source Files

The source files are organized into three subgroups:

  • Application
  • User Interface
  • Game


Figure 7. The TicTacPalm Project.

Application

When you generate a project using the project stationary, one of the files, Starter.cpp, contains the main program for the application. In the sample application, Starter.cpp is replaced by TicTacPalm.cpp. It has seven functions:

  • AppHandleEvent
  • AppStart
  • AppStop
  • RomVersionCompatible
  • AppEventLoop
  • TicTacPalmMain
  • PilotMain

The contents of the first three are new in TicTacPalm.cpp. Of the other four, only TicTacPalmMain has been changed from the original StarterMain in Starter.cpp. One line of StarterMain has been commented out.

   FrmGotoForm ( MainForm );

In the sample application, the first form is loaded in AppStart rather than the main program.

Three variables of file scope are defined in TicTacPalm.cpp. Their referenced objects are created in AppStart and deleted in AppStop. They are:

static CGameBoardForm *fGameBoardForm = NULL;
static CGameInfoForm *fGameInfoForm = NULL;
static CTicTacGame *fGame = NULL;

The first two handle the two different forms. The game object contains all the data about the current game that is in dynamic RAM. Each of the form objects is initialized with a pointer to the game.

AppStart
// static 
Err AppStart(void)
{
   UInt16 prefsSize;
   TicTacPalmPreferenceType preferences;
 
   // Read the saved preferences / saved-state information.
   fGame = new CTicTacGame ();
   fGameBoardForm = new CGameBoardForm( fGame );
   fGameInfoForm = new CGameInfoForm ( fGame );

   prefsSize = sizeof(TicTacPalmPreferenceType);
   if (PrefGetAppPreferences(appFileCreator, appPrefID,
       &preferences, &prefsSize, true) != noPreferenceFound)
   {
      fGame->SetPrefs ( &preferences );
   }
   // Display the first form.
   FrmGotoForm ( fGame->GetForm() );      
   return errNone;
}
Listing 1. AppStart

The preferences record stores information used to reconstruct the last state of the program so that it will appear to the user that the program wasn't interrupted at all. If there is no preferences record to read, a default preferences record is constructed.

AppStart creates fGame and initializes it with the values in the preferences. It then brings forward a form, chosen according to the last state of the application as recorded in the preferences.

AppHandleEvent
// static 
Boolean AppHandleEvent(EventPtr eventP)
{   
   return eventP->eType == frmLoadEvent && 
      LForm :: Load ( eventP->data.frmLoad.formID );
}
Listing 2. AppHandleEvent

AppHandleEvent sends the load event to the LForm class (which is discussed later).

AppStop
// static 
void AppStop(void)
{
   FrmCloseAllForms ();
    TicTacPalmPreferenceType preferences;
   fGame->GetPrefs ( &preferences );
   PrefSetAppPreferences (appFileCreator, appPrefID,
       appPrefVersionNum, &preferences, 
      sizeof (preferences), true);
   
   if ( fGameBoardForm )
      delete fGameBoardForm;
   if ( fGameInfoForm )
      delete fGameInfoForm;
   if ( fGame )
      delete fGame;
}
Listing 3. AppStop

When the application receives a stop event, AppStop saves into the preference record the state of things as recorded in gGame, and deletes the objects that were created in AppStart.

Supporting Forms

Associated with an active form is a callback function that processes events sent to the form. The classes CGameBoardForm and CGameInfoForm handle events for the two types of forms. These classes are subclasses of LForm.

LForm :: DispatchEvent
// static
Boolean LForm :: DispatchEvent ( EventType *inEvent )
{
   //   Identify the event type and respond to it accordingly.

   Boolean handled = false;
   if ( sActiveLForm )
      switch ( inEvent->eType )
      {
         case frmOpenEvent :
            // Do follow up after open event
            FrmDrawForm ( FrmGetActiveForm() );
            handled = sActiveLForm->Open();
            break;
         case frmCloseEvent :
            // Close stuff before closing if necessary
            handled = sActiveLForm->Close();
            break;
         case menuEvent :
            handled = sActiveLForm->
               DoCommand ( inEvent->data.menu.itemID, NULL );
            break;   
         case ctlSelectEvent:
            handled = sActiveLForm->
               DoSelect ( inEvent->data.ctlSelect.controlID, 
                  NULL );
            break;
         default :
            handled = false;
            break;
      }
   return handled;
}
Listing 4. LForm::Dispatch Event
   LForm :: Load

// static
Boolean LForm ::    Load ( UInt16 inFormID )
// 
// The Palm OS has requested that a form be loaded.
// If the form can be loaded, mark the appropriate
// LForm
{
   Boolean handled = false;
   FormPtr formPointer = FrmInitForm ( inFormID );
   if ( formPointer )
   {
      sActiveLForm = IDToLForm ( inFormID );
      if ( sActiveLForm )
      {
         FrmSetActiveForm ( formPointer );
         FrmSetEventHandler ( formPointer, &DispatchEvent );
         handled = true;
      }
   }
   return handled;
}
Listing 5. LForm::Load

When the system function FrmGotoForm is called to switch forms, the application sends the function to the class function, LForm::Load. That function finds the LForm object that will handle the form according to the ID requested. It establishes the event handler for that class as the handler for form events.

When events arrive for a form, the class function LForm::DispatchEvent, sends the event to one of the virtual functions, DoCommand, DoSelect, Open or Close. Subclasses need only override these functions.

Game Board Form

This form displays the name of the current game, the playing surface and two buttons. One button clears the board. The other switches to the game information form.

CGameForm::Open
// virtual
Boolean CGameBoardForm :: Open ()
{
   // Set up all the gadgets as CSquareGadget objects
   FormPtr form = FrmGetActiveForm();
   UInt16 numberOfObjects = FrmGetNumberOfObjects ( form );
   for ( UInt16 j = 0; j < numberOfObjects; j++ )
   {
      FormObjectType *objectPointer = 
         (FormObjectType*)FrmGetObjectPtr( form, j );
   FormObjectKind objectKind = FrmGetObjectType ( form, j );
      if ( objectKind == frmGadgetObj )
      {
         FormGadgetType *gadget = (FormGadgetType*)objectPointer;
   CSquareGadget *squareGadget = new CSquareGadget ( mID, j,
            gadget, mGame );
      }
   }
   GameNameType gameName;
   mGame->GetName ( gameName );
   SetFieldText ( GameBoardGameNameField, gameName );
   FrmDrawForm ( form );
   
   // Also mark the game that this form is active.   
   mGame->SetForm ( GameBoardForm );
   return true;
 }
Listing 6. CGameBoardForm::Open

Open creates a collection of objects to handle the gadgets that will be used in the form. These objects are not deleted in CGameBoardForm. They delete themselves as we'll see in the section on gadgets.

The list of controls in the form is scanned to find controls that are gadgets. As each gadget is found, a LgameSquareGadget object is created to handle its events.

CGameBoardForm::DoSelect
// virtual
Boolean CGameBoardForm :: DoSelect ( UInt16 inCommand, void */*inParam*/ )
{
   Boolean handled = false;
   switch ( inCommand )
   {
      case GameBoardClearButton:
         mGame->Clear();
         FormPtr activeForm = FrmGetActiveForm();
         FrmDrawForm ( activeForm );
         handled = true;
         break;
      case GameBoardNewButton:
         FrmGotoForm(GameInfoForm);
         handled = true;
         break;         
   }   
   return handled;
}
Listing 7. CGameForm::Open

DoSelect handles the two buttons: Clear and New. For the Clear button, it just tells the game object to clear its data and then redraws the form. FrmDrawForm will call the gadget handlers to do the drawing. The gadget handlers will then retrieve any needed data from the game object as part of the redraw operation.

GameInfoForm

With this form the user enters a name for the game. When the user taps on the OK button, the current game is cleared and a new game is started and control is switched to the game board form. If the user taps Cancel, control also returns to the game board form, but the name is not changed and the game resumes.

The user could also switch to another application. In that case, the name field is restored as it was, with a name that the user has changed, but not confirmed by tapping OK.

CGameInfoForm :: Open
// virtual 
Boolean CGameInfoForm :: Open()
{
   // Inform the game that the info form is active.
   mGame->SetForm ( GameInfoForm );

   GameNameType name;
   // If the last open form was a game info form then use the name 
   // that appeared on that form.
   mGame->GetUnconfirmedName ( name );
      
   // Otherwise use the name of current game.
   if ( StrLen ( name ) == 0 )
      mGame->GetName ( name );
   mGame->SetUnconfirmedName ( name );
   SetFieldText ( GameInfoNameFieldField, name );
   FieldType *field = GetField ( GameInfoNameFieldField );
   FldDrawField ( field );

   mOKWasPressed = false;
   mCancelWasPressed = false;
   return true;
}
Listing 8. CGameInfoForm::Open

If the form was replaced because the user changed applications, then an unconfirmed name is stored in the game object and the unconfirmed name will be loaded into the field. Otherwise, the name of the current game will be loaded into the field.

CGameInofForm::Close
// virtual 
Boolean CGameInfoForm :: Close()
{
   GameNameType name;
   if ( mCancelWasPressed )
      // Don't care about what was in the field.
      mGame->SetUnconfirmedName ( "" );
   else
   {
      if ( GetFieldText ( GameInfoNameFieldField, name ) )
         if ( mOKWasPressed )
         {
            mGame->Clear();
            mGame->SetName ( name );
            mGame->SetUnconfirmedName ( "" );
         }
         else // switched applications
            mGame->SetUnconfirmedName ( name );
   }
   // Return false to tell the OS to clean up the form
   // in the usual way after we have extracted the info.
   return false;   
}
Listing 9 CGameInfoForm::Close

Close sets the name and unconfirmed name fields in the game according to two flags: mOKWasPressed and mCancelWasPressed.

CGameInfoForm::Open
// virtual
Boolean CGameInfoForm :: DoSelect ( UInt16 inCommand, void */*inParam*/ )
{
   Boolean handled = false;
   switch ( inCommand )
   {
      case GameInfoOKButton:
         mOKWasPressed = true;
         FrmGotoForm(GameBoardForm); 
         handled = true;
         break;
      case GameInfoCancelButton:
         mCancelWasPressed = true;
         FrmGotoForm(GameBoardForm);
         handled = true;
         break;         
   }
   return handled;
}
Listing 10 CGameInfoForm::DoSelect

DoSelect sets the appropriate flags according to which button was pressed. It then switches to the game board form.

A Gadget for a Square

Instead of handling events as is done for forms, a gadget callback handles commands. One type of command is an event command. So you need two levels of dispatch: one at a higher-level for events, and one at a lower-level for commands.

In TicTacPalm, there is a gadget for each of the nine squares in the game board. The class LGadget handles the dispatch. Its subclass, CSquareGadget contains the code to respond to actions.

The Palm data type for gadgets contains a field for application defined data. Unfortunately, the argument passed to the handler is a pointer to an opaque structure. Palm documentation warns you not to directly access its fields. But they don't provide the accessor functions needed to conveniently get the application data, making the data field unusable. In Palm OS 4.0 an accessor function was added but it doesn't work with all the commands that may be sent to the handler.

The workaround is to search all the gadgets looking for a matching pointer.

LGadget::LGadget
LGadget ::   LGadget ( UInt16 inFormID, UInt16 inGadgetIndex,
   FormGadgetType *inGadget )
: mFormID ( inFormID ), mGadgetIndex ( inGadgetIndex ), 
   mGadget ( inGadget )
{
   FormType *formPointer = FrmGetFormPtr ( mFormID );   
   FrmSetGadgetHandler ( formPointer, 
mGadgetIndex, &DispatchCommand );
   mPrevious = sLast;
   sLast = this;
}
Listing 11. LGadget::LGadget

The constructor stores a pointer to the data structure for the form and sets a class function of LGadget to be the handler.

LGadget.cpp
// static
Boolean LGadget :: DispatchCommand ( FormGadgetType *inGadget, 
   UInt16 inCommand, void *inParam )
{
   Boolean success = false;
   LGadget *lGadget = FindLGadget ( inGadget );   
   success = lGadget && lGadget->DoCommand ( inCommand, inParam );
   return success;
}
Listing 12. LGadget::DispatchCommand

DispatchCommand is the class function that receives the command. It finds the LForm object to handle that command.

LGadget::DoCommand
Boolean LGadget :: DoCommand ( UInt16 inCommand, void *inParam )
{
   Boolean handled = false;
   switch ( inCommand )
   {
      case formGadgetDrawCmd :
         Draw();
         mGadget->attr.visible = true;
         handled = true;
         break;
      case formGadgetHandleEventCmd :
         handled = HandleEvent ( (EventType*)inParam );
         break;
      case formGadgetDeleteCmd :
         delete this;
         handled = true;
         break;
      case formGadgetEraseCmd :
         handled = false;
         break;
      default :
         handled = false;
         break;
   }
   return handled;
}
Listing 13. LGadget::DoCommand

DoCommand determines the type of command and forwards it along. The delete command gets sent immediately before the enclosing form gets deleted. This handler deletes the LForm object in response.

         LGadget::HandleEvent 
Boolean LGadget :: HandleEvent ( EventType *inEvent )
{
   Boolean handled = false;
   switch ( inEvent->eType )
   {
      case frmGadgetEnterEvent :
         // A pen down event has been passed to the form.
         handled = true;
         Tapped();
         break;
      case frmGadgetMiscEvent :
         // The application, not the system generates
         // this event. Not used in the sample code,
         // include to allow for future modifications.
         handled = false;
         break;
      default :
         // Should never get here.
         handled = false;
         break;
   }
   return handled;
}
Listing 13. LGadget::HandleEvent

CSquareget

For each of the squares in the game there is a gadget. For each gadget there is an LSquareGadget object. Squares are numbered 0 through 8. When the user taps a game square, Tapped checks if the square is empty and tries to play an X at that location. It displays the current object and then tells the LSquareGadget object corresponding to the O response to draw itself.

Images of the X and O characters, and an empty field image, are stored as bitmaps. Draw copies the appropriate bitmap to the active form.

CSquareGadget::CSquareGadget
CSquareGadget ::   CSquareGadget( UInt16 inFormID, 
   UInt16 inGadgetIndex, FormGadgetType *inGadget, CTicTacGame *inGame )
: LGadget ( inFormID, inGadgetIndex, inGadget )
{
   FormPtr form = FrmGetFormPtr ( inFormID );
   UInt16 objectID = FrmGetObjectId ( form, inGadgetIndex );
   mGame = inGame;
   mSquare = SquareNumber ( objectID );
   sSquareGadget[mSquare] = this;
}
Listing 14. CSquareGadget::CSquareGadget

The constructor uses the object ID of the gadget to determine the number of the square. That is the number that will be used in the game logic to interpret the play.

CSquareGadget::Draw
// virtual
void CSquareGadget :: Draw ()
{
   Coord x;
   Coord y;
   FormType *formPointer = FrmGetFormPtr ( mFormID );
   FrmGetObjectPosition 
      ( formPointer, mGadgetIndex, &x, &y );
   UInt16 bitmapID = EmptySquareBitmap;
   PlayerT contents = mGame->Get ( mSquare );
   switch ( contents )
   {
      case kX :
         bitmapID = XBitmap;
         break;
      case kO :
         bitmapID = OBitmap;
         break;
      case kPlayerEmpty :
      default:
         bitmapID = EmptySquareBitmap;
         break;
   }
   MemHandle resource = DmGetResource 
      ( bitmapRsc, bitmapID );
   BitmapPtr bitmapResource = 
      (BitmapPtr)MemHandleLock ( resource );
   Err error = DmGetLastErr();
   if ( !error && bitmapResource )
   {
      WinDrawBitmap ( bitmapResource, x, y );
      error = MemHandleUnlock ( resource );
      DmReleaseResource ( resource );
   }
}
Listing 15. CSquareGadget::Draw

The content of the square is determined from the game object. According to the content, select a resource with the X, O or blank bitmap.

CSquareGadget::Tapped
// virtual
void CSquareGadget :: Tapped ()
{
   UInt16 oResponse = kNoPosition;
   if ( mGame && mGame->Get ( mSquare ) == kPlayerEmpty )
   {
      oResponse = mGame->PlayX ( mSquare );
      Draw();
   }
   if ( oResponse != kNoPosition )
   {
      CSquareGadget *cSquareGadget = 
         sSquareGadget[oResponse];
      cSquareGadget->Draw();
   }
}
Listing 16. CSquareGadget::Tapped

When the user taps on a game square, this function responds by displaying an X. It gets the next O move from the game object and calls the draw function for the CSquareGadget for that square.

Conclusion

Developing for the Palm is easy to do with CodeWarrior and POSE. The Palm OS is continually evolving and making many housekeeping chores easier, such as improved bitmap support. Right now, the API reminds me of the early days of Macintosh. The application programmer must be concerned with a lot of messy details. I find that I miss having an application framework and feel the need to head in that direction.

The need to be able to fold up the tent whenever the user switches applications adds an interesting twist to the job of programming for the Palm OS. Here, too, an application framework would come in handy.

TicTacPalm is a fairly simple application. Additional features might include the ability to store and recall games, beam games between devices, and back games up to a desktop machine. Feel free to use the code presented here as the basis for writing an improved version of TicTacPalm, or possibly some other game.

References

The Palm web site contains tons of information and links to related sites.

http://www.Palm OS.com/dev/

To register a creator code just click on the list item Creator ID. As on Mac OS, creator IDs uniquely identify applications.

The documentation in the Palm SDK is fairly good. There is a Reference manual and a Programmers' Guide. Some third-party books on Palm programming are available. Palm Programming by Neil Rhodes and Julie McKeehan is a good introduction, though the material in it is quite dated.

Metrowerks has a demo version of CodeWarrior for Palm that you can use to get started, available at:

http://www.metrowerks.com/products/palm/demo

There is a free online course in Palm programming offered by Metrowerks. It's at:

http://www.codewarrioru.com/CodeWarriorU

Credits

Thanks to Victoria Leonard for graphic resources. Thanks to Bob Ackerman and Victoria Leonard for reviewing the text.


Danny Swarzman writes programs in JavaScript, Java, C++ and other languages. He also plays Go and grows potatoes. Contact him with comments and job offers. dannys@stowlake.com, http://www.stowlake.com

 
AAPL
$119.00
Apple Inc.
+1.40
MSFT
$47.75
Microsoft Corpora
+0.28
GOOG
$540.37
Google Inc.
-0.71

MacTech Search:
Community Search:

Software Updates via MacUpdate

HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
Carbon Copy Cloner 4.0.3 - Easy-to-use b...
Carbon Copy Cloner backups are better than ordinary backups. Suppose the unthinkable happens while you're under deadline to finish a project: your Mac is unresponsive and all you hear is an ominous,... Read more
ForeverSave 2.1.3 - Universal auto-save...
ForeverSave auto-saves all documents you're working on while simultaneously doing backup versioning in the background. Lost data can be quickly restored at any time. Losing data, caused by... Read more

Latest Forum Discussions

See All

Make Way for Fat Chicken, from the Maker...
Make Way for Fat Chicken, from the Makers of Scrap Squad Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Relevant Games has announced they will be releasing their reverse tower defense game, | Read more »
Tripnary Review
Tripnary Review By Jennifer Allen on November 26th, 2014 Our Rating: :: TRAVEL BUCKET LISTiPhone App - Designed for the iPhone, compatible with the iPad Want to create a travel bucket list? Tripnary is a fun way to do exactly that... | Read more »
Ossian Studios’ RPG, The Shadow Sun, is...
Ossian Studios’ RPG, The Shadow Sun, is Now Available for $4.99 Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Mmmm, Tasty – Having the Angry Birds for...
The very first Angry Birds debuted on iOS back in 2009. When you sit back and tally up the number of Angry Birds games out there and the impact they’ve had on pop culture as a whole, you just need to ask yourself: “How would the birds taste... | Read more »
Rescue Quest Review
Rescue Quest Review By Jennifer Allen on November 26th, 2014 Our Rating: :: PATH BASED MATCH-3Universal App - Designed for iPhone and iPad Guide a wizard to safety by matching gems. Rescue Quest might not be an entirely original... | Read more »
You Can Play the Final Chapter of Lone W...
You Can Play the Final Chapter of Lone Wolf: Dawn Over V’taag Right Now Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Swords of Anima (Games)
Swords of Anima 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: A new tactical turn-based RPG experience. Command the Savior Rex Squad in an epic journey of courage and deception. Can you... | Read more »
Audio Defence: Zombie Arena
Audio Defence: Zombie Arena By Lee Hamlet on November 26th, 2014 Our Rating: :: DRAGS ITS FEETUniversal App - Designed for iPhone and iPad From the makers of Papa Sangre comes a defense game that forces players to listen carefully... | Read more »
Tales from the Borderland​s Will be Comi...
Tales from the Borderland​s Will be Coming to iOS by the End of the Year Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Telltale Games has announced | Read more »
Sunburn! Review
Sunburn! Review By Campbell Bird on November 26th, 2014 Our Rating: :: DON'T DIE ALONEUniversal App - Designed for iPhone and iPad Platform through the depths of space to make sure your entire crew dies together in this satisfying... | Read more »

Price Scanner via MacPrices.net

2014 1.4GHz Mac mini on sale for $449, save $...
 B&H Photo has the new 1.4GHz Mac mini on sale for $449.99 including free shipping plus NY tax only. Their price is $50 off MSRP, and it’s the lowest price available for this new model. Adorama... Read more
Early Black Friday pricing on 27-inch 5K iMac...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more
Early Black Friday sale prices on iPad Air 2,...
 MacMall is discounting iPad Air 2s by up to $75 off MSRP as part of their Black Friday sale. Shipping is free: - 16GB iPad Air WiFi: $459 $40 off - 64GB iPad Air WiFi: $559 $40 off - 128GB iPad Air... Read more
Early Black Friday MacBook Air sale prices, $...
 MacMall has posted early Black Friday MacBook Air sale prices. Save $101 on all models for a limited time: - 11″ 1.4GHz/128GB MacBook Air: $798 - 11″ 1.4GHz/256GB MacBook Air: $998 - 13″ 1.4GHz/... Read more
Why iPhone 6 Tablet/Laptop Cannibalization Is...
247wallst.com blogger Douglas A. McIntyre noted last week that according to research posted on the Applovin blog site the iPhone 6 is outselling the iPhone 6 Plus by a wide margin . Hardly a surprise... Read more
Worldwide Tablet Growth Expected to Slow to 7...
The global tablet market is expected to record massive deceleration in 2014 with year-over-year growth slowing to 7.2%, down from 52.5% in 2013, according to a new forecast from International Data... Read more
Touchscreen Glove Company Announces New Produ...
Surrey, United Kingdom based TouchAbility specializes in design and manufacture of a wide variety of products compatible with touchscreen devices including smartphones, tablets and computers. Their... Read more
OtterBox Alpha Glass Screen Protectors for iP...
To complement the bigger, sharper displays on the latest Apple devices, OtterBox has introduced Alpha Glass screen protectors to the iPhone 6 and iPhone 6 Plus. The fortified glass screen protectors... Read more
Early Black Friday Mac Pro sale, 6-Core 3.5GH...
 B&H Photo has the 6-Core 3.5GHz Mac Pro on sale today for $3499 including free shipping plus NY sales tax. Their price is $500 off MSRP, and it’s the lowest price available for this model from... Read more
Early Black Friday sale price: 15-inch 2.2GHz...
 B&H Photo has the 2014 15″ 2.2GHz Retina MacBook Pro on sale today for $1699.99. Shipping is free, and B&H charges NY sales tax only. Their price is $300 off MSRP, equalling Best Buy’s price... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.