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.
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.
|
Related Reading
Palm OS Programming |
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.
Here are the main types used by the Palm OS:
UInt32Int32UInt16Int16UInt8Int8BooleanCharWCharErrerrNone signifies no error.CoordMemPtrMemHandleThe 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.
|
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 |
|
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.
|
Standard C routine |
Palm OS routine |
Additional information |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
Doesn't pad with extra null terminators. |
|
|
|
|
|
|
|
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. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Limited subset of sprintf, for example, no |
|
|
|
Limited subset of svprintf, for example, no |
|
|
|
Although you'll probably use handles instead. |
|
|
|
|
|
|
|
|
|
|
|
Warning: the last two parameters have been reversed! |
|
|
|
|
That sums up the important conventions you need to know about to create a Palm application.
When the Palm OS wants to communicate with an application that may not be running, it calls the application's PilotMain routine.
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.
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 #
pragmaunused(launchParameters)is an indication to CodeWarrior that the parameterlaunchParametersis 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.
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
FrmGotoFormto 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.
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( );
}
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:
EvtGetEvent, and then dispatch that event to one of four event handlers, each of which gets a chance to handle the event.
true, it has handled the event and we don't process it any further.
appStopEvent, at which time we exit the function and clean things up in AppStop.
|
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);
}
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
nilEventat 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.
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.
|
|
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.
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.
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.
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.)
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(&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.
|
Related Reading Palm OS Programming |
Return to the Wireless DevCenter
Copyright © 2007 O'Reilly Media, Inc.