Wireless DevCenter    
 Published on Wireless DevCenter (http://www.oreillynet.com/wireless/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: Palm OS Programming, 2nd Edition

Structure of a Palm Application, Part 2

by Neil Rhodes and Julie McKeehan

A Simple Application

Now it's time to apply what you just learned about an application's interaction with the OS, its PilotMain routine, and the event loop to an actual application. We are using OReilly Starter, the application we first discussed in Chapter 4.

What the Application Does--Its User Interface

Our OReilly Starter application has two forms. On the first form, there are two buttons. Pressing the first one causes the Palm device to beep. Pressing the second button switches the view to the second form. The second form has a single button that returns to the previous form (see Figure 5-3). This is all there is to our simple application.

Figure 5-3. The OReilly Starter application

An Overview of the Source Files

Here are the source files that make up this application (as opposed to the tools-specific files like CodeWarrior .mcp files or PRC-Tools .def files).

Main.c
This file contains the main entry point (PilotMain) of the application. It also includes the event loop, application startup and shutdown code, and the code to load forms as needed.


MainForm.c
This contains the code that handles everything that occurs in the first form.


SecondForm.c
This contains the code for the second form.


Utils.c
This contains some utility routines that can be used throughout the application. You can include this same source file in all your applications.


Resources.rcp
This PilRC file contains the UI elements (the forms, form objects, and the application name).


ResourceDefines.h
This file defines constants for all the application's resources. This file is included by the .c files, as well as Resources.rcp.


In This Series

Structure of a Palm Application, Part 3
In this third excerpt from "Palm OS Programming, 2nd Edition," Neil Rhodes and Julie McKeehan discuss how the operating system communicates with an application when it's not running. This is the third in a series of three excerpts designed to familiarize developers with the structure of Palm apps.

Structure of a Palm Application, Part 1
In this excerpt from "Palm OS Programming, 2nd Edition," Neil Rhodes and Julie McKeehan introduce the terminology and conventions of a Palm application. This is the first in a series of three excerpts designed to familiarize developers with the structure of Palm apps.

MainForm.h
This declares the event handler for the main form. This is included by both Main.c and MainForm.c.


SecondForm.h
This declares the event handler for the second form. It is also included by both Main.c and SecondForm.c.


Constants.h
This file contains the defined constants used throughout the application.


Utils.h
This declares the utility functions in Utils.c.


The Source File of Main.c

Main.c starts with the #include files (see Example 5-4).

Example 5-4: First part of Main.c: #defines and #includes

#define DO_NOT_ALLOW_ACCESS_TO_INTERNALS_OF_STRUCTS
#include <BuildDefines.h>
#ifdef DEBUG_BUILD
#define ERROR_CHECK_LEVEL ERROR_CHECK_FULL
#endif
#include <PalmOS.h>
#include "ResourceDefines.h"
#include "MainForm.h"
#include "SecondForm.h"
#include "Utils.h"
#include "Constants.h"

Related Reading

Palm OS Programming
The Developer's Guide
By Neil Rhodes, Julie McKeehan

TIP: PalmOS.h is an include file that contains most of the standard Palm OS include files. By default, it defines an ERROR_CHECK_LEVEL of ERROR_CHECK_PARTIAL, which is suitable for a release build. It doesn't, however, provide the checking we'd like for a debug build (see Chapter 7 for more information on debug builds). Thus, if we're compiling a DEBUG_BUILD, we redefine ERROR_CHECK_LEVEL. We must #include BuildDefines.h first, in order to obtain the definition of ERROR_CHECK_FULL.

We define DO_NOT_ALLOW_ACCESS_TO_INTERNALS_OF_STRUCTS to help with good coding practices. This will cause the 4.0 SDK to generate a compile-time error for us if we try to access fields within OS structures directly, rather than through appropriate API calls. You should always use the APIs, rather than fool around within an OS structure directly.

Example 5-5 shows the remainder of Main.c.

Example 5-5: Remaining functions in Main.c

static Boolean AppHandleEvent(EventPtr event)
{
  UInt16 formId;
  FormPtr form;
 
  if (event->eType == frmLoadEvent) {
    // Load the form resource.
    formId = event->data.frmLoad.formID;
    form = FrmInitForm(formId);
    ErrFatalDisplayIf(!form, "Can't initialize form");
    FrmSetActiveForm(form);
 
    // Set the event handler for the form.  The handler of the currently
    // active form is called by FrmHandleEvent each time it receives 
    // an event.
    switch (formId) {
    case MainForm:
      FrmSetEventHandler(form, MainFormHandleEvent);
      break;
 
    case SecondForm:
      FrmSetEventHandler(form, SecondFormHandleEvent);
      break;
 
    default:
      ErrFatalDisplay("Invalid Form Load Event");
      break;
 
    }
    return true;
  } else 
    return false;
}
 
static void AppEventLoop(void)
{
  Err error;
  EventType event;
 
  do {
    EvtGetEvent(&event, evtWaitForever);
    
    if (! SysHandleEvent(&event))
      if (! MenuHandleEvent(0, &event, &error))
        if (! AppHandleEvent(&event))
          FrmDispatchEvent(&event);
 
  } while (event.eType != appStopEvent);
}
 
 
static Err AppStart(void)
{
   return errNone;
}
 
 
static void AppStop(void)
{
  FrmCloseAllForms(  );
}
 
UInt32 PilotMain(UInt16 launchCode, MemPtr launchParameters, 
  UInt16 launchFlags)
{
#pragma unused(launchParameters)
  Err error;
 
  switch (launchCode) {
  case sysAppLaunchCmdNormalLaunch:
    error = RomVersionCompatible (kOurMinVersion, launchFlags);
    if (error) 
      return error;
    error = AppStart(  );
    if (error) 
rror;
      
    FrmGotoForm(MainForm);
    AppEventLoop(  );
    AppStop(  );
    break;
 
  default:
    break;
  }
  
  return errNone;
}

kOurMinVersion is defined as Version 3.0 of the Palm OS in Constants.h.

The Form Files of the Application

Main.c is the source file containing routines responsible for handling the main form (see Example 5-6).

Example 5-6: Main.c

/*
 Copyright (c) 2000-2001, Neil Rhodes and Julie McKeehan
 		neil@pobox.com
 All rights reserved.
 
From the book "Palm OS Programming (2nd edition)" by O'Reilly.
 
Permission granted to use this file however you see fit.
*/
 
#define DO_NOT_ALLOW_ACCESS_TO_INTERNALS_OF_STRUCTS
#include <BuildDefines.h>
#ifdef DEBUG_BUILD
#define ERROR_CHECK_LEVEL ERROR_CHECK_FULL
#endif
#include <PalmOS.h>
#include "ResourceDefines.h"
#include "MainForm.h"
 
 
static void MainFormInit(FormPtr form)
{
#pragma unused(form)
  // Warning-- don't do any drawing in this routine.
  // Also, don't call FrmSetFocus from here (it must be called *after*
  // FrmDrawForm).
}
 
static void MainFormDeinit(FormPtr form)
{
#pragma unused(form)
}
 
 
Boolean MainFormHandleEvent(EventPtr event)
{
  Boolean handled = false;
  FormPtr form;
 
  switch (event->eType) 
  {
  case frmOpenEvent:
    form = FrmGetActiveForm(  );
    MainFormInit(form);
    FrmDrawForm(form);
    // Here's where you'd add a call to FrmSetFocus.
    handled = true;
    break;
      
      
  case ctlSelectEvent:
  switch (event->data.ctlSelect.controlID) {
    case MainBeepButton:
      SndPlaySystemSound(sndWarning);
      handled = true;
      break;
        
    case MainGotoSecondFormButton:
      FrmGotoForm(SecondForm);
      handled = true;
      break;
    }
    break;
 
  case frmCloseEvent:
    MainFormDeinit(FrmGetActiveForm(  ));
    handled = false;
    break;
 
  default:
    break;
  }  
  return handled;
}

The event handler for the main form handles three different kinds of events:

The MainFormInit and MainFormDeinit routines are there because it is quite common to need to do some initialization when a form opens. You will often need to do some cleanup when a form closes. This is where such things should happen.

The MainFormHandleEvent handles the ctlSelectEvent by looking to see what control was chosen:

The EventType data structure

In order to handle events within your form's event handler, you have to become familiar with the EventType data structure (the data that comes back from EvtGetEvent). Here are the fields within that structure:

eType
The type of the event (an enumeration: examples are menuEvent, penDownEvent, keyDownEvent, and so on).
penDown
Was the stylus down when this event occurred?
tapCount
For OS 3.5 and later, the number of successive taps. On a double-tap, you'll get two events. The first will have a tapCount of 1, and the second a tapCount of 2. Note: the value in this field is undefined prior to the 3.5 version of the OS.
screenX
The horizontal location of the stylus (in screen coordinates).
screenY
The vertical location of the stylus (in screen coordinates).

Each event type also has specific associated data. For example, menu events have the menu item ID, control events have the control ID, key events have the character. This data is stored in a union of structures, with a separate structure defined for each kind of event. The union of structures' name is data. Within that union, the name of the structure is the name of the event type, without the event suffix. For example, the data for a menuEvent is found in the data.menu structure, the data for a ctlSelectEvent is found in data.ctlSelect.

Example 5-7 shows SecondForm.c, which contains the code responsible for the second form.

Example 5-7: SecondForm.c

/*
 Copyright (c) 2000-2001, Neil Rhodes and Julie McKeehan
   neil@pobox.com
 All rights reserved.
 
From the book "Palm OS Programming (2nd edition)" by O'Reilly.
 
Permission granted to use this file however you see fit.
*/
 
#define DO_NOT_ALLOW_ACCESS_TO_INTERNALS_OF_STRUCTS
#include <BuildDefines.h>
#ifdef DEBUG_BUILD
#define ERROR_CHECK_LEVEL ERROR_CHECK_FULL
#endif
#include <PalmOS.h>
#include "ResourceDefines.h"
#include "SecondForm.h"
 
 
static void SecondFormInit(FormPtr form)
{
#pragma unused(form)
  // Warning-- don't do any drawing in this routine.
  // Also, don't call FrmSetFocus from here (it must be called *after*
  // FrmDrawForm).
}
 
static void SecondFormDeinit(FormPtr form)
{
#pragma unused(form)
}
 
 
Boolean SecondFormHandleEvent(EventPtr event)
{
  Boolean handled = false;
  FormPtr form;
 
 
  switch (event->eType) {
  case frmOpenEvent:
    form = FrmGetActiveForm(  );
    SecondFormInit(form);
    FrmDrawForm(form);
    // Here's where you'd add a call to FrmSetFocus.
    handled = true;
    break;
      
      
  case ctlSelectEvent:
    switch (event->data.ctlSelect.controlID) {
    case SecondGotoMainFormButton:
      FrmGotoForm(MainForm);
      handled = true;
      break;
    }
    break;
 
  case frmCloseEvent:
    SecondFormDeinit(FrmGetActiveForm(  ));
    handled = false;
    break;
 
  default:
    break;
  }
  
  return handled;
}

This code is very similar in structure to that of MainForm.c. The difference is what happens when a control is chosen.

The Utility and Resource Files

Here is a look at the utility and resource files that OReilly Starter uses.

Utils.c contains several utility routines in this source file, but we use only one in this application. Example 5-8 shows that sole routine.

Example 5-8: Utils.c (abridged to show only the routine we use)

/*
 Copyright (c) 2000-2001, Neil Rhodes and Julie McKeehan
    neil@pobox.com
 All rights reserved.
 
From the book "Palm OS Programming (2nd edition)" by O'Reilly.
 
Permission granted to use this file however you see fit.
*/
 
#define DO_NOT_ALLOW_ACCESS_TO_INTERNALS_OF_STRUCTS
#include <BuildDefines.h>
#ifdef DEBUG_BUILD
#define ERROR_CHECK_LEVEL ERROR_CHECK_FULL
#endif
#include <PalmOS.h>
#include "Utils.h"
#include "ResourceDefines.h"
 
Err RomVersionCompatible(UInt32 requiredVersion, UInt16 launchFlags)
{
  UInt32 romVersion;
 
  // See if we're on minimum required version of the ROM or later.
  FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
  if (romVersion < requiredVersion) {
    UInt16 safeToCallAlertFlags;
    
    safeToCallAlertFlags = 
      sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp;
    if ((launchFlags & (safeToCallAlertFlags)) == safeToCallAlertFlags) {
      FrmAlert (RomIncompatibleAlert);
    
      // Pilot 1.0 will continuously relaunch this application 
      //unless we switch to another safe one.
      if (romVersion < sysMakeROMVersion(2,0,0,sysROMStageRelease,0))
        AppLaunchWithCommand(sysFileCDefaultApp, 
          sysAppLaunchCmdNormalLaunch, NULL);
    }
    
    return (sysErrRomIncompatible);
  }
 
  return errNone;
}

You'll normally use this routine as-is in every application you write. It works by calling the Feature Manager (which contains runtime information about the Palm device) to find out what ROM version this Palm device is running. If the version is not earlier than the requested one, the routine returns no error. Otherwise, it will display an alert and return an error. Last of all, it deals with a potentially infinite relaunch problem in the 1.0 version of the OS by explicitly launching some other application.

The Resources.rcp PilRC file, shown in Example 5-9, can be compiled by either the PilRC command line, when using the PRC-Tools, or by the PilRC plug in, when using the CodeWarrior environment.

Example 5-9: Resources.rcp

#include "ResourceDefines.h"
 
APPLICATIONICONNAME 1000 "Starter"
 
ALERT ID RomIncompatibleAlert
CONFIRMATION
BEGIN
  TITLE "System Incompatible"
  MESSAGE "System Version 3.0 or greater " \
    "is required to run this application."
  BUTTONS "OK" 
END
 
FORM ID MainForm AT (0 0 160 160)
USABLE
BEGIN
  TITLE "OReilly Starter Main"
  BUTTON "Beep" ID MainBeepButton AT (40 100 AUTO AUTO)
  BUTTON "Goto Second Form" ID MainGotoSecondFormButton AT 
    (PrevLeft PrevBottom + 5 AUTO AUTO)
END
 
FORM ID SecondForm AT (0 0 160 160)
USABLE
BEGIN
  TITLE "OReilly Starter Second"
  BUTTON "Goto Main Form" ID SecondGotoMainFormButton AT 
    (40 130 AUTO AUTO)
END

This defines the name of the application as it appears in the Application launcher, the alert that's shown if run on an older system, and the two forms.

Header Files

Example 5-10 shows Constants.h, which defines two constants: our creator ID and the minimum version for which we'll run.

Example 5-10: Constants.h

#ifndef CONSTANTS_H
#define CONSTANTS_H
 
#define kAppFileCreator         '????'
 
// The minimum OS version we support.
#define kOurMinVersion  sysMakeROMVersion(3,0,0,sysROMStageRelease,0)
#endif

The next file, ResourceDefines.h, is included by both the .c files and the .rcp file to define the constants used by the UI (see Example 5-11).

Example 5-11: ResourceDefines.h

#define RomIncompatibleAlert           1001
 
#define MainForm                       2000 
#define MainBeepButton                 2001
#define MainGotoSecondFormButton       2002
 
#define SecondForm                     3000 
#define SecondGotoMainFormButton       3001

MainForm.h file defines the function that is exported from MainForm.c (see Example 5-12).

Example 5-12: MainForm.h

#ifndef MAINFORM_H
#define MAINFORM_H

Boolean MainFormHandleEvent(EventPtr event);

#endif

SecondForm.h file defines the function that is exported from SecondForm.c (see Example 5-13).

Example 5-13: SecondForm.h

#ifndef SECONDFORM_H
#define SECONDFORM_H
 
Boolean SecondFormHandleEvent(EventPtr event);
 
 
#endif

Utils.h exports some useful utility routines (see Example 5-14).

Example 5-14: Utils.h

#ifndef UTILS_H
#define UTILS_H
 
Err RomVersionCompatible(UInt32 requiredVersion, UInt16 launchFlags);
void *GetObjectPtr(FormPtr form, UInt16 objectID);
void *GetObjectPtrFromActiveForm(UInt16 objectID);
 
#endif

OReilly Starter Summary

In this simple application, we have all the major elements of any Palm application. In review, these are:

In the next installment, learn how the operating system communicates with an application when it's not running

Neil Rhodes and Julie McKeehan are experienced authors who, through their company, Calliope Enterprises, work closely with Palm Computing to develop new training materials, materials that are based on this book.

Palm OS Programming

Related Reading

Palm OS Programming
The Developer's Guide
By Neil Rhodes, Julie McKeehan

Return to the Wireless DevCenter

Copyright © 2009 O'Reilly Media, Inc.