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 1

by Neil Rhodes and Julie McKeehan

Chapter 5
Structure of an Application

Before you can write a Palm application, you need to know how it interacts with the OS and how it is organized. Prior to this discussion, we cover the standard terminology and naming conventions within the Palm OS. Once you know what the words mean, we can talk about the application and the OS.

We start with a discussion of how an application is structured to run on the Palm OS. You will learn that a Palm application is an event-driven system and that its routines are structured to handle various types of events. We will describe an application's life cycle--its starting, running, and closing. To help solidify your understanding of these points, we provide a simple application, OReilly Starter, which is a prototypical Palm application. We walk you through its organization (for example, its source files, utility files, and so on) and then show you the source code in its routines. While the application doesn't do much, it contains all of the standard routines and has the correct structure for any Palm application. You can use it as the starting point for your own work.

Typically, an application launches when a user opens it; you will learn how to handle this. We will also discuss other times the OS may access an application and what you need to do about it. These instances require you to structure your application so that it can provide information or launch as necessary. Lastly, there are some tricks you might want to add that allow shortcut access from within your application (for example, a hard button). Or, you might want to be a tyrant and take over the unit completely, denying access to other applications while your application is running.

Terminology

Like every operating system, the Palm OS has it own set of necessary terminology for you to learn. Much of it may be familiar to you already from other systems on which you have worked. We suggest that you skim through the list and concentrate on the items that are new to you. New and unique terms are listed first.

Form
An application form (what many people would think of as a window) that usually covers the entire screen (modal forms cover only the bottom part of the screen, however). A form optionally contains controls, text areas, and menus. In a Palm OS application there is only one active form allowed at a time. Chapter 8 covers forms in detail.


Related Reading

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

Window
A rectangular area in which things like dialog boxes, forms, and menus are drawn by the application. The Window Manager makes sure that windows display properly relative to each other (for example, it has the ability to restore the old contents when a window is closed).


Database
A collection of persistent memory chunks. There are two kinds:


Resource
A piece of data stored in a resource database. Each resource is identified by a resource type and number. Note that a Palm application is simply a collection of resources. Chapter 8 covers resources in more detail.


Record
A memory chunk identified by a unique record ID located in a database. Applications typically store data in record databases.


Event
A data structure that describes things that happen in an application. These can be low-level hardware events such as a pen down, pen up, or hardware key press. They can also be higher-level events such as a character entry, a menu item selection, or a software button press.


The Palm OS is an event-driven system. Only one application is open at a time. When that application is running, it runs an event loop that retrieves events and continues to handle them until the user starts another application.


Main event loop
The main loop of execution in an application that repeatedly retrieves events, and then acts on them.


Launch code
A parameter passed to an application that specifies what the application should do when that particular launch code is executed. An application typically handles more than one launch code. This is the communication method used between the OS and a non-running application and between applications.


Menu
Menus are stored in resources grouped together into menu bars and are displayed when the user taps the menu area. See Chapter 11 for more details.


Menu bar
A collection of menus stored in a resource. Each form can have a menu bar associated with it.


Dialog box
A window containing controls that require the user to make a decision. In other words, the dialog box must be dismissed (usually by tapping on one of its buttons) before the application can continue.


Alert
A simple dialog box with only an icon, text, and buttons.


Palm OS Conventions

There are also a variety of Palm coding conventions that are useful to know. There are type conventions and standard naming practices for everything from functions to managers. It is worth getting a clearer idea of what these are before wading knee-deep into your first coding project. These are the ones you should learn.

Types

Here are the main types used by the Palm OS:

UInt32
An unsigned 32-bit integer.


Int32
A signed 32-bit integer.


UInt16
An unsigned 16-bit integer.


Int16
A signed 16-bit integer.


UInt8
An unsigned 8-bit integer.


Int8
A signed 8-bit integer.


Boolean
A 1-byte true or false value.


Char
A 1-byte character that will only work on systems with 1-byte encodings.


WChar
A 2-byte character suitable for all encodings (including for Japanese, Chinese, and so on).


Err
A 2-byte integer used for errors. The value errNone signifies no error.


Coord
A signed 2-byte integer used to represent a screen or window coordinate.


MemPtr
Specifies a (4-byte) pointer to an allocated chunk in memory.


MemHandle
A (4-byte) reference to a relocatable chunk of memory (see Chapter 6 for more details).


Function and Manager Naming Conventions

The Palm OS uses mixed-case for names, with an initial uppercase for functions and types. Constants and enumerations begin with lowercase letters. The following snippet of code shows the conventions in action (emphasis shows functions, types, and enumerations):

FormPtr form;
UInt32  romVersion;
FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
FrmSetEventHandler(form, MyHandler);

The Palm OS is divided into functional areas called managers. Each manager has its own header file (for example Form.h). Every routine in that manager usually begins with a manager abbreviation (for example: EvtGetEvent is part of the Event Manager, declared in Event.h; FrmGotoForm is part of the Form Manager, declared in Form.h). Table 5-1 contains example abbreviations.

Table 5-1: Standard manager abbreviations

Abbreviation

Manager

Alm

Alarm Manager

Dm

Data Manager

Evt

Event Manager

Ftr

Feature Manager

Mem

Memory Manager

Snd

Sound Manager

Str

String Manager

Sys

System Manager

Txt

Text Manager

C Library Conventions

You will not normally use standard C library routines like strlen or memcpy. While these functions are available as libraries, using them in your code bloats the size of your application. Since space is always tight, you have been provided with another way to use such routines; you'll use the ones in ROM that are small and quick. Table 5-2 shows the Palm OS-equivalents for standard C routines.

Table 5-2: Equivalents to standard C routines

Standard C routine

Palm OS routine

Additional information

strlen

StrLen

 

strcpy

StrCopy

 

strncpy

StrNCopy

Doesn't pad with extra null terminators.

strcat

StrCat

 

strncat

StrNCat

Last parameter is the total length of the string (including null terminator) rather than the number of characters to copy. Doesn't append an extra null terminator if source string is empty.

strcmp

StrCompare

 

strncmp

StrNCompare

 

itoa

StrIToA

 

strchr

StrChr

 

strstr

StrStr

 

sprintf

StrPrintF

Limited subset of sprintf, for example, no %f.

svprintf

StrVPrintF

Limited subset of svprintf, for example, no %f.

malloc

MemPtrNew

Although you'll probably use handles instead.

free

MemPtrFree

 

memmove

MemMove

 

memset

MemSet

Warning: the last two parameters have been reversed!

memcmp

MemCmp

 

That sums up the important conventions you need to know about to create a Palm application.

The Palm OS and an Application

When the Palm OS wants to communicate with an application that may not be running, it calls the application's PilotMain routine.

The Main Routine: PilotMain

This is the main entry point into a Palm OS application; it is always a function named PilotMain. Given its responsibilities, it is worth looking at this routine in some detail. First, we'll start with the parameters, and then we'll show you the code. Following the code is a walk-through discussion of what is happening.

A quick look at PilotMain

The first parameter is the launch code, which specifies why the function is being called. Whenever your application is being opened normally, this parameter will be the constant sysAppLaunchCmdNormalLaunch. The second and third parameters are used when the application is opened at other times (see Example 5-1).

Example 5-1: Typical PilotMain (app-specific portions are emphasized)

UInt32 PilotMain(UInt16 launchCode, MemPtr launchParameters, 
  UInt16 launchFlags)
{
#pragma unused(launchParameters)
  Err error;

  switch (launchCode) {
  case sysAppLaunchCmdNormalLaunch:
    error = RomVersionCompatible(kOurMinVersion, launchFlags);
    if (error != errNone) 
      return error;
    error = AppStart(  );
    if (error != errNone) 
      return error;
      
    FrmGotoForm(MainForm);
    AppEventLoop(  );
    AppStop(  );
    break;

  default:
    break;
  }
  
  return errNone;
}

If the launch code is sysAppLaunchCmdNormalLaunch, we first check to make sure that the version of the device we are running on is one that we support (you'll have to define the minimum version appropriate for your application).

TIP:   A pragma is a compiler-specific directive. The #pragma unused(launchParameters) is an indication to CodeWarrior that the parameter launchParameters is not used in the function. With this in place, CodeWarrior won't be constantly warning us of the unused parameter. GCC (as it should) ignores the pragma.

While we will talk about each of the routines called in our PilotMain in greater detail in just a moment, briefly, this is what they do. First, we call our own routine, AppStart, which does application-specific initialization. The call to FrmGotoForm specifies that the MainForm will initially be displayed (if we left that out, the display would be blank since no form was opened). It will queue a frmLoadEvent in the Event Manager's event queue (we'll talk more about form events in Chapter 8).

Now, the application's motor--our event loop, AppEventLoop--runs until the user does something to close the application. At that point, we handle termination in AppStop.

The Startup Routine: AppStart

Here is where we handle all the standard opening and initialization of our application. In a typical application, this would include opening our databases and reading user preference information. In our skeleton application (OReilly Starter), we won't do anything.

Although it's common to name this routine AppStart, it's only a convention, not a requirement like the name of PilotMain.

TIP:   Note that we call FrmGotoForm in our PilotMain rather than here. This is important because an application will eventually need to support Find. In that case, another launch code will require us to initially open a different form. Because of this, we need FrmGotoForm to be in a location that allows us to switch between forms depending on how the application gets opened.

Since we have a very simple application, our AppStart does nothing:

static Err AppStart(void)
{
   return 0;
}

Note that we have a static declaration here for the benefit of CodeWarrior. If we leave off the static, then CodeWarrior will warn us that the function doesn't have a prototype.

CodeWarrior complains about this potential error because the function might have been declared in a header file that wasn't included in this file. Furthermore, that declaration might declare the function differently (for example, different numbers or types of parameters). Specifying that the function is static guarantees that the function won't be called from outside this file, thus no separate function declaration is required.

In general, always add a static when defining functions that are only used within a single file. This is the purpose for the keyword after all.

The Closing Routine: AppStop

Normally, in AppStop we handle all the standard closing operations, such as closing our database, saving the current state in preferences, and so on. Because we are creating such a simple application, we don't actually have to do those things. However, like all applications, ours needs to make sure that any open forms are closed. FrmCloseAllForms will do that closing.

static void AppStop(void)
{
  FrmCloseAllForms(  );
}

The Main Event Loop

In PilotMain, after the initialization there is a call to the one main event loop, AppEventLoop. In brief, this is what happens in this loop:

Example 5-2 shows the code for a typical AppEventLoop.

Example 5-2: Typical AppEventLoop

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);
}
Handling events with EvtGetEvent

This Event Manager routine's sole job is to get the next event from the queue. It takes as a second parameter a timeout value (in ticks--SysTicksPerSecond returns the units, which is 100 on all devices as of mid-2001). EvtGetEvent returns when either an event has occurred, or when the timeout value has elapsed (in which case it fills in an event code of nilEvent).

TIP:   EvtGetEvent can actually return with a nilEvent at any time, regardless of whether the timeout value has elapsed.

We don't have anything to do until an event occurs (this application has no idle-time processing to do), so we pass the evtWaitForever constant, specifying that we don't want a timeout.

The Event Queue and Application Event Loop

At this point, it is worth looking in more detail at the events that are received from EvtGetEvent. Events can be of all different types: anything from low-level to high-level ones. In fact, one useful way to look at a Palm application is simply as an event handler--it hands all sorts of events off to various managers, which in turn may post new events back to the queue where they will get handled by other event handlers. We will discuss more sophisticated examples of this later (see "Scenarios" later in this chapter), but for now we will look at a very simple set of events to get an idea of how all this works together. Imagine the user has our application open and taps the stylus on the screen in the area of the silk-screened menu button. The first time through the event queue the SysHandleEvent routine handles the event, interprets it, and creates a new event that gets put back in the queue (see Figure 5-1).

When this new event comes through the loop, it will get passed through SysHandleEvent and on to the MenuHandleEvent as it is now recognizable as a menu request (see Figure 5-2). MenuHandleEvent will display the menu bar and drop down one of the menus. If the user now taps outside of the menu, the menus will disappear.

Figure 5-1. An event in the event loop

Figure 5-2. A regurgitated event in the event loop

If a menu item is selected, however, then a new event is generated and posted to the queue. This event is retrieved by the event loop where it is passed through SysHandleEvent, and then on to MenuHandleEvent, and continues until some code handles the menu item. Given the way this process works, you can see that the different managers are interested in different types of events. Keeping this in mind, let's now return to our code and look at the event loop and the four routines in it.

The Four Routines of the Main Event Loop

You have briefly seen the four routines and their order in the main event loop. Here is a more detailed look at the responsibilities of each routine.

SysHandleEvent

if (! SysHandleEvent(&event))

The first routine in the loop is always SysHandleEvent as it provides functionality common to all Palm applications. For example, it handles key events for the built-in application buttons. It does so by posting an appStopEvent to tell the current application to quit. After the application quits, the system launches the desired application.

It also handles pen events in the silk-screened area (the graffiti input area and the silk-screened buttons). For example, if the user taps on Find, SysHandleEvent will completely handle the Find, returning only when the Find is done.

SysHandleEvent likewise handles the Power key, dealing with putting the device to sleep and/or turning backlighting on or off, depending on the duration of the Power key press.

MenuHandleEvent

if (! MenuHandleEvent(0, &event, &error))

The second routine in our event loop is MenuHandleEvent. As you might have imagined, the MenuHandleEvent handles events involving menus. These events occur when a user taps on the following:

As would be expected, it closes the menu and menu bar if the user taps on a menu item. At this point, it posts a menu event that will be retrieved in a later call to EvtGetEvent.

The first parameter to MenuHandleEvent specifies which menu to handle (0 means the current menu and is what you'll always pass). The last parameter is a pointer to an error return result; note, however, that virtually all applications (including ours) ignore the error result.

Unlike all other managers, the Menu Manager routines use a four-character prefix (Menu) rather than the three characters common to the others. In addition, unlike SysHandleEvent and FrmDispatchEvent, which take only the event and return a Boolean, MenuHandleEvent takes two extra parameters. (We get the feeling that whoever wrote the Menu Manager skipped some of the meetings on API design that the rest of the OS team attended.)

AppHandleEvent

if (! AppHandleEvent(&event))

The third routine, AppHandleEvent, is also a standard part of the event loop and is responsible for loading forms and associating an event handler with the form. This routine handles any events that it wants to process application-wide (as opposed to the majority of events, which are form-specific). This code is not part of the OS; you write it. However, it will be very similar from application to application. Example 5-3 shows a typical example of the routine.

Example 5-3: Typical AppHandleEvent (app-specific portions are emphasized)

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;
}

A frmLoadEvent is a request to load a particular form. It is our responsibility to load the requested form (the OS won't load a form automatically). We initialize the specified form ID (FrmInitForm) and then make it the one-and-only active form (FrmSetActiveForm). Finally, we set the form's event handler (FrmSetEventHandler). If you create a new form, you'll write an event handler for it, and then add a new case to the switch statement, setting the event handler.

FrmDispatchEvent

FrmDispatchEvent(&event);

This fourth and last routine in the event loop is the one that indirectly provides form-specific handling. FrmDispatchEvent calls FrmHandleEvent, which provides standard form functionality (for example, a pen-down event on a button highlights the button, a pen-up on a button posts a ctlSelectEvent event to the event queue). Cut, copy, and paste in text fields are other examples of functionality handled by FrmHandleEvent.

In order to provide form-specific handling, FrmDispatchEvent calls the form's installed event handler first before calling FrmHandleEvent. If the event handler returns true, no further processing happens. If it returns false, FrmDispatchEvent will then call FrmHandleEvent, which will provide the standard functionality. Thus, your event handler always gets the first crack at events occurring within a form. Here's pseudocode for FrmDispatchEvent to give you an idea of what it will look like:

Boolean FrmDispatchEvent(EventType *event)
{
  Boolean handled = result of calling Form's event handler;
  if (handled)
    return true;
  else
    return FrmHandleEvent(event);
}
In the next installment create a simple Palm application

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.