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 3

by Neil Rhodes and Julie McKeehan

Related Reading

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

Editors Note: In this third and final 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.

Other Times Your Application is Called

You know how an application works within the OS and now you know about the organizational structure and content of the various source files used to create an application. The last remaining piece of the puzzle is other times the OS communicates with an application. Let's turn to the details of this.

The Palm OS makes a distinction between communicating with the active application and communicating with a possibly nonactive application. In this first case, the active application is busy executing an event loop, and can be communicated with by posting events to the event queue. As shown earlier in our example, this was how our application got closed; the appStopEvent was posted to the event queue. When the active application gets that event, it quits.

Because there are other times that your application gets called by the Palm OS, there needs to be a way to communicate with it in those instances as well. First, let's look at a partial list of the circumstances under which the system might want to talk to your application:

In all these cases, a communication must take place to a nonactive or closed application. The question is: how does the system do this? The answer: its PilotMain routine is called with different launch codes.

Launch Codes

Within the Palm OS, it is the launch code that specifies to the application which of the previously mentioned circumstances exists and what the application needs to do. These codes arrive at the application's PilotMain routine by way of its launchCode parameter. Here are some common launch codes:

sysAppLaunchCmdFind
This code tells the application to look up a particular text string and return information about any matching data. Called by the system when the user does a Find.

sysAppLaunchCmdGoTo
This code tells the application to open if it isn't already open and then to go to the specified piece of data. Called by the system when the user taps on a found item.

sysAppLaunchCmdNormalLaunch
As we have already seen, this code opens the application normally.

sysAppLaunchCmdSystemReset
Sent after a reset occurs.

TIP:   In the 4.0 OS and prior ones, some launch codes are sent to every installed application (for example, after a sync, a reset, or a time change). When a user has many applications installed, this broadcast can be slow. In the future, they may only be sent to those applications that have registered for that particular notification. This registration is available with a Palm OS 3.5 call SysNotifyRegister.

Launch Flags

In This Series

Structure of a Palm Application, Part 2
In this second excerpt from "Palm OS Programming, 2nd Edition," Neil Rhodes and Julie McKeehan build on the terminology and conventions they introduced in the first excerpt and walk us through a sample application. This is the second 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.

The launch flags specify important information about how the application is being executed. Here are some examples:

Scenarios

To help clarify the relationship between the application and the times when the system calls it, let's look at examples of when this happens and the flow of code.

Normal launch

Your application gets a launch code of sysAppLaunchCmdNormalLaunch when it's opened, and launch flags of hexadecimal 0x8e, specifying the following, OR-ed together:

sysAppLaunchFlagDataRelocated
This is a private Palm OS flag.

sysAppLaunchFlagUIApp
The application should show a UI.

sysAppLaunchFlagNewGlobals
The application has just been allocated global variables.

sysAppLaunchFlagNewStack
The application has been allocated a new stack.

Find when another application is active

When the Memo Pad is the active application, and the user does a Find, the Find Manager calls the PilotMain of every installed application. When our application is called, the launch code is 1 (sysAppCmdFind), and the launch flag is 0 (no globals, no UI).

Find when your application is active

Things happen differently when we do a Find with our application already open. In this case, PilotMain is still called with the same launch code: sysAppLaunchCmdFind, but now the launch flag is different. It is 0x10 (sysAppLaunchFlagSubCall), specifying that the OReilly Starter application is already open and running. This means that global variables have been allocated and initialized, and we are running as an indirect subroutine call from the application.

Figure 5-4 shows the stack trace when the Find is done in this case. The stack trace shows that PilotMain is called (indirectly) from our AppEventLoop (which itself is called from our original PilotMain).

Figure 5-4.The stack trace when doing a Find from within our own application
 

Examples

There are other communications between the Palm OS and your application that you might want to handle in your code. Particular events, or overall access might be important for you to control. In any case, here are events some applications want to handle:

Let's take a look at the code required to handle each of these instances.

Handling a Pen-Down Event

Normally, you will not handle pen-down events directly, but instead handle higher-level events like ctlSelectEvent. However, occasionally, applications will want to be on the lookout for a penDown event. An example is the Address Book: tapping and releasing on the display of an address in the display view switches to the edit view.

The source code to the Address Book is part of the Palm OS 3.5 SDK. The RecordViewHandleEvent in Address.c from that example contains the following case:

case penDownEvent:
         handled = RecordViewHandlePen(event);
         break;

RecordViewHandlePen handles penDown events in the display-only view of the record (see Example 5-15).

Example 5-15: RecordViewHandlePen in the Address Book source


static Boolean RecordViewHandlePen (EventType * event) {
   Boolean      handled = false;
   FormPtr      frm;
   RectangleType r;
   Int16        x, y;
   Boolean      penDown;

   // If the user taps in the RecordViewDisplay take her to
the Edit View.
   frm = FrmGetActiveForm(  );
   FrmGetObjectBounds(frm, FrmGetObjectIndex(frm,
RecordViewDisplay), &r);
   if (RctPtInRectangle (event->screenX,
event->screenY, &r))
      {
      do 
         {
         PenGetPoint (&x, &y, &penDown);
         } while (penDown);
      
      if (RctPtInRectangle (x, y, &r))
         FrmGotoForm (EditView);
         
      handled = true;
      }
      
   return handled;
}

The calls to the Form Manager routines FrmGetActiveForm and FrmGetObjectBounds yield the rectangle containing the address display area. RctPtInRectangle checks to see whether the pen-tap location (event->screenX and event->screenY) are within those bounds. Note that it then enters a loop calling PenGetPoint until the user releases the stylus. If the user lets go within those same bounds, the code switches to the edit view.

Handling a Graffiti Character

To show you an example of handling a graffiti character, we turned to the Reptoids game (one of the samples in the Palm OS 3.5 SDK). While the game is running, entering a "t" character displays the amount of time spent playing on-screen.

The Rocks.c file has the following check in MainViewHandleEvent:

else if (event->eType == keyDownEvent)
    {
    // Time spent playing.       (Quick code at this
point.)
    if (event->data.keyDown.chr == 't')
        {
        // ... Code that displays game-time is here.    

        }
 
    return true;
    }

Note that the routine checks the incoming character by looking within the event: event->data.keyDown.chr.

Overriding Hard-Button Behavior

Some applications want to redirect the hard button presses (the Date Book key, for instance) for their own use. An obvious example would be games. Pressing a hard button generates a keyDownEvent and as such can be looked for by an application. Indeed, the keyDownEvent is sent for all of these:

In all the instances, a special modifier bit (the commandKeyMask) is set in the modifiers associated with that key. This distinguishes it from a normal Graffiti character.

However, for all but the scroll buttons, the system handles the keyDownEvent, and doesn't allow the form's event handler to deal with it.

Now, let's see how we can use this information to modify our source code to our advantage. We will make an application where tapping on the Date Book key doesn't bring up the Date Book, but instead does something app-specific.

First off, we need to avoid calling SysHandleEvent when that key is pressed (see Example 5-16).

Example 5-16: AppEventLoop that doesn't call SysHandleEvent for taps on the Date Book key

static void AppEventLoop(void)
{
  Err error;
  EventType event;

  do {
    Boolean isDatebookKey;
    
    EvtGetEvent(&event, evtWaitForever);
    
    isDatebookKey = (event.eType == keyDownEvent)
      &&
(TxtCharIsHardKey(event.data.keyDown.modifiers, 
        event.data.keyDown.chr))
      && (event.data.keyDown.chr == vchrHard1);

    if (isDatebookKey || ! SysHandleEvent(&event))
      if (! MenuHandleEvent(0, &event, &error))
        if (! AppHandleEvent(&event))
          FrmDispatchEvent(&event);

  } while (event.eType != appStopEvent);
}

Note that we figure out whether it is a hard key, and then whether it is the Date Book key (vchrHard1). If it is, we don't call SysHandleEvent, and so the normal processing for that character won't happen. However, the event will be passed to FrmDispatchEvent (after being ignored by MenuHandleEvent and AppHandleEvent), and from there to our event handler. Here's code in MainFormHandleEvent that switches to the second form if the Date Book key is pressed:

switch (event->eType) 
  {
  case keyDownEvent:
    if (TxtCharIsHardKey(event->data.keyDown.modifiers,

        event->data.keyDown.chr)
      && (event->data.keyDown.chr == vchrHard1))
{
      FrmGotoForm(SecondForm);
      handled = true;
    }
    break;

Controlling the Exiting of an Application

Some turnkey applications take over the machine and don't allow any other applications to run. For example, a Palm OS device carried by delivery people might only run a delivery application; it's locked for any other purpose.

TIP:  Admittedly, this is a very rare UI. A standard Palm application should always quit when the user requests another application (by pressing one of the hard buttons, for instance). Standard applications should not have an explicit UI for quit, however. Users implicitly quit an application by starting another.

How do you write an application that takes control of the Palm OS unit and won't let go? It's simple. In order to keep other applications from running, you rudely refuse to exit from your main event loop. As a result, the application never quits (except on a reset). All the OS can do is post an appStopEvent to the event queue, requesting (nay, strongly urging!) your application to quit. It's up to the application itself to actually quit, though.

In the simplest case, you might just code your event loop so that it never exits (see Example 5-17).

Example 5-17: AppEventLoop that never exits

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 (true);  // Don't ever quit.
}

This will ensure that your application, once running, will never exit. It'll silently ignore an appStopEvent that is posted to the event queue.

Another scenario is an application that won't quit unless given a specific command. Some turnkey applications provide a mechanism (perhaps a password-protected button or menu item) to quit the application and open up the unit to the rest of the Palm UI. The easiest way to implement this is to have a global variable specifying whether appStopEvents should be ignored. When the button is pressed, the application can post an appStopEvent, and set the variable. Here's how the event loop would change:

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 && AppShouldStop(  )));
}

AppShouldStop just returns the value of the global:

Boolean gShouldStop = false;
 
Boolean AppShouldStop(  )
{
  return gShouldStop;
}

Now, when the specific action occurs (like pressing a button or choosing a menu item), a routine MakeAppStop would be called to set the global, and post an appStopEvent to the event queue (see Example 5-18).

Example 5-18: MakeAppStop that causes the application to quit

void MakeAppStop(  )
{
  UInt32 romVersion;
 
  gShouldStop = true;
  FtrGet(sysFtrCreator, sysFtrNumROMVersion,
&romVersion);
  if (romVersion <
sysMakeROMVersion(2,0,0,sysROMStageRelease,0)) {
    AppLaunchWithCommand(sysFileCDefaultApp, 
      sysAppLaunchCmdNormalLaunch, NULL);
  } else
    PostAppStopEvent(  ); // Launch previous app.
}

WARNING:   Palm OS 1.0 relaunches the last application, so the code must explicitly launch an application in this case. (The default application is the one that is shown after a reset.) Any post-1.0 OS launches the previous application, so the code just posts an appStopEvent.

Here's the code that posts the appStopEvent. Note that whenever you create an event yourself, you should zero out the entire structure so that unused fields are zero:

static void PostAppStopEvent(  )
{
  EventType event;
  
  // Set all unused fields to 0.
  MemSet(&event, sizeof(event), 0);
	
  event.eType = appStopEvent;
  EvtAddEventToQueue(&event);
}

What to Remember

In this chapter, we have given you a description of important terminology, standards, and a description of how an application interacts with the Palm OS on a device. Most importantly, you should remember the following:

From all of this information, you should now be well on your way to understanding this application architecture. In the following chapters, you will be using this information to move beyond our simple OReilly Starter application to create a full-featured 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.