Documentation  |   Table of Contents   |  < Previous   |  Next >   |  Index

2    Application Startup and Stop

Palm OS® Programmer's Companion

Volume I

     

On desktop computers, an application starts up when a user launches it and stops when the user chooses the Exit or Quit command. These things occur a little bit differently on the Palm OS® handheld. A Palm OS application does launch when the user requests it, but it may also launch in response to some other user action, such as a request for the global find facility. Palm OS applications don't have an Exit command; instead they exit when a user requests another application.

This chapter describes how an application launches, how an application stops, and the code you must write to perform these tasks properly. It also covers notifications, which is another way for the system to launch your code when certain events occur. Notifications are available in later releases of the Palm OS. This chapter covers:

This chapter does not cover the main application event loop. The event loop is covered in Chapter 3, "Event Loop."

Launch Codes and Launching an Application ^TOP^

An application launches when it receives a launch code. Launch codes are a means of communication between the Palm OS and the application (or between two applications).

For example, an application typically launches when a user presses one of the buttons on the device or selects an application icon from the application launcher screen. When this happens, the system generates the launch code sysAppLaunchCmdNormalLaunch, which tells the application to perform a full launch and display its user interface.

Other launch codes specify that the application should perform some action but not necessarily become the current application (the application the user sees). A good example of this is the launch code used by the global find facility. The global find facility allows users to search all databases for a certain record, such as a name. In this case, it would be very wasteful to do a full launch—including the user interface—of each application only to access the application's databases in search of that item. Using a launch code avoids this overhead.

Each launch code may be accompanied by two types of information:

  • A parameter block, a pointer to a structure that contains several parameters. These parameters contain information necessary to handle the associated launch code.
  • Launch flags indicate how the application should behave. For example, a flag could be used to specify whether the application should display UI or not. (See "Launch Flags" in the Palm OS Programmer's API Reference.)

A complete list of all launch codes is provided at the end of this chapter in the section "Launch Code Summary." That section contains links into where each launch code is described in the Palm OS Programmer's API Reference.

Responding to Launch Codes ^TOP^

Your application should respond to launch codes in a function named PilotMain. PilotMain is the entry point for all applications.

When an application receives a launch code, it must first check whether it can handle this particular code. For example, only applications that have text data should respond to a launch code requesting a string search. If an application can't handle a launch code, it exits without failure. Otherwise, it performs the action immediately and returns.

Listing 2.1 shows parts of PilotMain from the Datebook application as an example. To see the complete example, go to the examples folder in the Palm OS SDK and look at the file Datebook.c.

Listing 2.1  PilotMain in Datebook.c


UInt32 PilotMain (UInt16cmd, void*cmdPBP, 
UInt16launchFlags) 
{ 
return DBPilotMain(cmd, cmdPBP, launchFlags); 
} 
 
static UInt32 DBPilotMain (UInt16cmd, void*cmdPBP, 
UInt16launchFlags) 
{ 
UInt16 error; 
Boolean launched; 
 
// This app makes use of PalmOS 2.0 features.It will crash  
// if run on an earlier version of PalmOS. Detect and warn  
// if this happens, then exit. 
error = RomVersionCompatible (version20, launchFlags); 
if (error) 
return error; 
 
// Launch code sent by the launcher or the datebook  
// button. 
if (cmd == sysAppLaunchCmdNormalLaunch) { 
error = StartApplication (); 
if (error) return (error); 
 
FrmGotoForm (DayView); 
EventLoop (); 
StopApplication (); 
} 
 
 
// Launch code sent by text search. 
else if (cmd == sysAppLaunchCmdFind) { 
Search ((FindParamsPtr)cmdPBP); 
} 
 
 
// This launch code might be sent to the app when it's  
// already running if the user hits the "Go To" button in  
// the Find Results dialog box. 
else if (cmd == sysAppLaunchCmdGoTo) { 
launched = launchFlags & sysAppLaunchFlagNewGlobals; 
if (launched) { 
error = StartApplication (); 
if (error) return (error); 
 
GoToItem ((GoToParamsPtr) cmdPBP, launched); 
 
EventLoop (); 
StopApplication (); 
else 
GoToItem ((GoToParamsPtr) cmdPBP, launched); 
} 
 
// Launch code sent by sync application to notify the  
// datebook application that its database has been synced. 
// ... 
// Launch code sent by Alarm Manager to notify the  
// datebook application that an alarm has triggered. 
// ... 
// Launch code sent by Alarm Manager to notify the  
// datebook application that is should display its alarm  
// dialog. 
// ... 
// Launch code sent when the system time is changed. 
// ... 
// Launch code sent after the system is reset. We use this  
// time to create our default database if this is a hard  
// reset 
// ... 
// Launch code sent by the DesktopLink server when it  
// creates a new database.  We will initialize the new  
// database. 
return (0); 
} 

Responding to Normal Launch ^TOP^

When an application receives the launch code sysAppLaunchCmdNormalLaunch, it begins with a startup routine, then goes into an event loop, and finally exits with a stop routine. (The event loop is described in Chapter 3, "Event Loop." The stop routine is shown in the section "Stopping an Application" at the end of this chapter.)

During the startup routine, your application should perform these actions:

  1. Get system-wide preferences (for example for numeric or date and time formats) and use them to initialize global variables that will be referenced throughout the application.
  2. Find the application database by creator type. If none exists, create it and initialize it.
  3. Get application-specific preferences and initialize related global variables.
  4. Initialize any other global variables.

As you saw in Listing 2.1, the Datebook application example responds to sysAppLaunchCmdNormalLaunch by calling a function named StartApplication. Listing 2.2 shows the StartApplication function.

Listing 2.2  StartApplication from Datebook.c


static UInt16 StartApplication (void) 
{ 
UInt16 error = 0; 
Err err = 0; 
UInt16 mode; 
DateTimeType dateTime; 
DatebookPreferenceType prefs; 
SystemPreferencesType sysPrefs; 
UInt16 prefsSize; 
 
// Step 1: Get system-wide preferences.  
PrefGetPreferences (&sysPrefs); 
// Determime if secret records should be  
// displayed. 
HideSecretRecords = sysPrefs.hideSecretRecords; 
 
if (HideSecretRecords) 
mode = dmModeReadWrite; 
else 
mode = dmModeReadWrite | dmModeShowSecret; 
 
// Get the time formats from the system preferences. 
TimeFormat = sysPrefs.timeFormat; 
 
// Get the date formats from the system preferences. 
LongDateFormat = sysPrefs.longDateFormat; 
ShortDateFormat = sysPrefs.dateFormat; 
 
// Get the starting day of the week from the system  
// preferences. 
StartDayOfWeek = sysPrefs.weekStartDay; 
 
 
// Get today's date. 
TimSecondsToDateTime (TimGetSeconds(), &dateTime); 
Date.year = dateTime.year - firstYear; 
Date.month = dateTime.month; 
Date.day = dateTime.day; 
 
 
// Step 2. Find the application's data file.  If it  
// doesn't exist, create it. 
ApptDB = DmOpenDatabaseByTypeCreator(datebookDBType, 
sysFileCDatebook, mode); 
if (! ApptDB) { 
error = DmCreateDatabase (0, datebookDBName,  
sysFileCDatebook, datebookDBType, false); 
if (error) return error; 
 
ApptDB =  
DmOpenDatabaseByTypeCreator(datebookDBType, 
sysFileCDatebook, mode); 
if (! ApptDB) return (1); 
 
error = ApptAppInfoInit (ApptDB); 
if (error) return error; 
} 
 
 
// Step 3. Get application-specific preferences.  
// Read the preferences/saved-state information. There is  
// only one version of the DateBook preferences so don't  
// worry about multiple versions. 
prefsSize = sizeof (DatebookPreferenceType); 
if (PrefGetAppPreferences (sysFileCDatebook,  
datebookPrefID, &prefs, &prefsSize, true)  
!= noPreferenceFound) { 
DayStartHour = prefs.dayStartHour; 
DayEndHour = prefs.dayEndHour; 
AlarmPreset = prefs.alarmPreset; 
NoteFont = prefs.noteFont; 
SaveBackup = prefs.saveBackup; 
ShowTimeBars = prefs.showTimeBars; 
CompressDayView = prefs.compressDayView; 
ShowTimedAppts = prefs.showTimedAppts; 
ShowUntimedAppts = prefs.showUntimedAppts; 
ShowDailyRepeatingAppts =  
prefs.showDailyRepeatingAppts; 
} 
 
// Step 4. Initialize any other global variables.  
TopVisibleAppt = 0; 
CurrentRecord = noRecordSelected;	 
 
 
// Load the far call jump table. 
FarCalls.apptGetAppointments = ApptGetAppointments; 
FarCalls.apptGetRecord = ApptGetRecord; 
FarCalls.apptFindFirst = ApptFindFirst; 
FarCalls.apptNextRepeat = ApptNextRepeat; 
FarCalls.apptNewRecord = ApptNewRecord; 
FarCalls.moveEvent = MoveEvent; 
 
return (error); 
} 

Responding to Other Launch Codes ^TOP^

If an application receives a launch code other than sysAppLaunchCmdNormalLaunch, it decides if it should respond to that launch code. If it responds to the launch code, it does so by implementing a launch code handler, which is invoked from its PilotMain function.

In most cases, when you respond to other launch codes, you are not able to access global variables. Global variables are generally only allocated after an application receives sysAppLaunchCmdNormalLaunch (see Listing 2.2) or sysAppLaunchCmdGoTo; so if the application hasn't received either of these launch codes, its global variables are usually not allocated and not accessible. In addition, if the application has multiple code segments, you cannot access code outside of segment 0 (the first segment) if the application has no access to global variables.

There is one other case where an application may have access to its global variables (and to code segments other than 0). This is when an application is launched with the code sysAppLaunchCmdURLParams. If this launch code results from a palm URL, then globals are available. If the launch code results from a palmcall URL, then globals are not available. The URL is passed to your application in the launch parameter block.


NOTE: Static local variables are stored with the global variables on the system's dynamic heap. They are not accessible if global variables are not accessible.

Checking launch codes is generally a good way to determine if your application has access to global variables. However, it actually depends on the setting of the launch flags that are sent with the launch code. In particular, if the sysAppLaunchFlagNewGlobals flag is set, then your application's global variables have been allocated on this launch. This flag is set by the system and isn't (and shouldn't be) set by the sender of a launch code.


Boolean appHasGlobals = launchFlags & sysAppLaunchFlagNewGlobals; 

There's one case where this flag won't be set and your application will still have access to global variables. This is when your application is already running as the current application. In this case, its global variables have already been allocated through a previous launch.

If your application receives a launch code other than sysAppLaunchCmdNormalLaunch or sysAppLaunchCmdGoTo, you can find out if it is the current application by checking the launch flags that are sent with the launch code. If the application is the currently running application, the sysAppLaunchFlagSubCall flag is set. This flag is set by the system and isn't (and shouldn't be) set by the sender of a launch code.


Boolean appIsActive = launchFlags & sysAppLaunchFlagSubCall; 

Launching Applications Programmatically ^TOP^

Applications can send launch codes to each other, so your application might be launched from another application or it might be launched from the system. An application can use a launch code to request that another application perform an action or modify its data. For example, a data collection application could instruct an email application to queue up a particular message to be sent.


TIP: In Palm OS 4.0 and higher, there are other ways for applications to communicate. See the section "When to Use the Helper API" to help you decide which method to use.

Sending a launch code to another application is like calling a specific subroutine in that application: the application responding to the launch code is responsible for determining what to do given the launch code constant passed on the stack as a parameter.

To send a launch code to another application, use the system manager function SysAppLaunch. Use this routine when you want to make use of another application's functionality and eventually return control of the system to your application. Usually, applications use it only for sending launch codes to other user-interface applications.

For example, you would use this function to request that the built in Address Book application search its databases for a specified phone number and return the results of the search to your application. When calling SysAppLaunch do not set launch flags yourself—the SysAppLaunch function sets launch flags appropriately for you.

An alternative, simpler method of sending launch codes is the SysBroadcastActionCode call. This routine automatically finds all other user-interface applications and calls SysAppLaunch to send the launch code to each of them.

When an application is called using SysAppLaunch, the system considers that application to be the current application even though the application has not switched from the user's perspective. Thus, if your application is called from another application, it can still use the function SysCurAppDatabase to get the card number and database ID of its own database.

If you want to actually close your application and open another application, use SysUIAppSwitch instead of SysAppLaunch. This routine notifies the system which application to launch next and feeds an application-quit event into the event queue. If and when the current application responds to the quit event and returns, the system launches the new application.

When you allocate a parameter block to pass to SysUIAppSwitch, you must call MemPtrSetOwner to grant ownership of the parameter block chunk to the OS (your application is originally set as the owner). If the parameter block structure contains references by pointer or handle to any other chunks, you also must set the owner of those chunks by calling MemPtrSetOwner or MemHandleSetOwner. If you don't change the ownership of the parameter block, it will get freed before the application you're launching has a chance to use it.

In Palm OS 3.0 and higher, you can also use the Application Launcher to launch any application. For more information, see the section "Application Launcher" in the "User Interface" chapter.


WARNING! Do not use the SysUIAppSwitch or SysAppLaunch functions to open the Application Launcher application.

Creating Your Own Launch Codes ^TOP^

The Palm OS contains predefined launch codes, which are listed in Table 2.1 at the end of this chapter. In addition, developers can create their own launch codes to implement specific functionality. Both the sending and the receiving application must know about and handle any developer-defined launch codes.

The launch code parameter is a 16-bit word value. All launch codes with values 0–32767 are reserved for use by the system and for future enhancements. Launch codes 32768–65535 are available for private use by applications.

Stopping an Application ^TOP^

An application shuts itself down when it receives the event appStopEvent. Note that this is an event, not a launch code. The application must detect this event and terminate. (You'll learn more about events in the next chapter.)

When an application stops, it is given an opportunity to perform cleanup activities including closing databases and saving state information.

In the stop routine, an application should first flush all active records, then close the application's database, and finally save those aspects of the current state needed for startup. Listing 2.3 is an example of a StopApplication routine from Datebook.c.

Listing 2.3  StopApplication from Datebook.c


static void StopApplication (void) 
{ 
DatebookPreferenceType prefs; 
 
// Write the preferences / saved-state information. 
prefs.noteFont = NoteFont; 
prefs.dayStartHour = DayStartHour; 
prefs.dayEndHour = DayEndHour; 
prefs.alarmPreset = AlarmPreset; 
prefs.saveBackup = SaveBackup; 
prefs.showTimeBars = ShowTimeBars; 
prefs.compressDayView = CompressDayView; 
prefs.showTimedAppts = ShowTimedAppts; 
prefs.showUntimedAppts = ShowUntimedAppts; 
prefs.showDailyRepeatingAppts = ShowDailyRepeatingAppts; 
 
// Write the state information. 
PrefSetAppPreferences (sysFileCDatebook, datebookPrefID,  
datebookVersionNum, &prefs, sizeof  
(DatebookPreferenceType), true); 
 
// Send a frmSave event to all the open forms. 
FrmSaveAllForms (); 
 
// Close all the open forms. 
FrmCloseAllForms (); 
 
// Close the application's data file. 
DmCloseDatabase (ApptDB); 
} 

Notifications ^TOP^

On systems where the Notification Feature Set is present, your application can receive notifications and launch when certain system-level events or application-level events occur. Notifications are similar to application launch codes, but differ from them in two important ways:

  • Notifications can be sent to any code resource, such as a shared library or a system extension (for example, a hack installed with the HackMaster program). Launch codes can only be sent to applications. Any code resource that is registered to receive a notification is called a notification client.
  • Notifications are only sent to applications or code resources that have specifically registered to receive them, making them more efficient than launch codes. Many launch codes are sent to all installed applications to give each application a chance to respond.

The Palm OS system and the built-in applications send notifications when certain events occur. See the "Notification Summary" in this chapter for a complete list.

It's also possible for your application to create and broadcast its own notifications. However, doing so is rare. It's more likely that you'll want to register to receive the predefined notifications or that you'll broadcast the predefined sysNotifyHelperEvent described in the "Helper Notifications" section.

Three general types of event flow are possible using the notification manager:

  • Single consumer

    Each client is notified that the event has occurred and handles it in its own way without modifying any information in the parameter block.

  • Collaborative

    The notification's parameter block contains a handled flag. Clients can set this flag to communicate to other clients that the event has been handled, while still allowing them to receive the notification. An example of this is the sysNotifyAntennaRaisedEvent for Palm VII series handhelds. A client might decide to handle the antenna key down event and in this case, sets handled to true to inform other clients that the event has been handled.

  • Collective

    Each client can add information to the notification's parameter block, allowing the data to be accumulated for all clients. This style of notification could be used, for example, to build a menu dynamically by letting each client add its own menu text. The sysNotifyMenuCmdBarOpenEvent is similar to this style of notification.

Registering for a Notification ^TOP^

To receive notification that an event has occurred, you must register for it using the SysNotifyRegister function. Once you register for a notification, you remain registered until the system is reset or until you explicitly unregister for this notification using SysNotifyUnregister.

To register an application for the HotSync® notification, you'd use a function call similar to the one in Listing 2.4.

Listing 2.4  Registering an application for a notification


SysNotifyRegister(myCardNo, appDBID, sysNotifySyncStartEvent,  
NULL, sysNotifyNormalPriority, myDataP); 

If you are writing a shared library instead of an application and you want to be notified about the HotSync event, your call to SysNotifyRegister looks slightly different. See Listing 2.5.

Listing 2.5  Registering a shared library for a notification


SysNotifyRegister(myCardNo, shlibDBID, 
sysNotifySyncStartEvent, SyncNotifyHandler, 
sysNotifyNormalPriority, myDataP); 

The parameters you pass to the SysNotifyRegister function specify the following:

  • The first two parameters are the card number and database ID for the prc file. Be sure you're not passing the local ID of the record database that your application accesses. You use the record database's local ID more frequently than you do the application's local ID, so this is a common mistake to make.
  • sysNotifySyncStartEvent specifies that you want to be informed when a HotSync operation is about to start. There is also a sysNotifySyncFinishEvent that specifies that a HotSync operation has ended.
  • The next parameter specifies how the notification should be received. This is where Listing 2.4 and Listing 2.5 differ.

    Applications use NULL for this parameter to specify that they should be notified through the application launch code sysAppLaunchCmdNotify. As with all other launch codes, the system passes this to the application's PilotMain function.

    The shared library has no PilotMain function and therefore no way to receive a launch code, so it passes a pointer to a callback function. Only use a callback function if your code doesn't have a PilotMain.

    Note that it's always necessary to pass the card number and database ID of your prc file even if you specify a callback function.

  • sysNotifyNormalPriority means that you don't want your code to receive any special consideration when receiving the notification. Notifications are broadcast synchronously in priority order. The lower the number you specify here, the earlier you receive the notification in the list.

    In virtually all cases, you should use sysNotifyNormalPriority. If you absolutely must ensure that your code is notified in a certain order (either before most notifications or after most notifications), be sure to leave some space between priority values so that your code won't collide with the system's handling of notifications or with another application's handling of notifications. Never use the extreme maximum or minimum allowed value. In general, Palm recommends using a value whose least significant bits are 0 (such as 32, 64, 96, and so on).

  • myDataP is a pointer to any data you need to access in your notification handler function. As with most launch codes, sysAppLaunchCmdNotify does not provide access to global variables, so you should use this pointer to pass yourself any needed data.

After you've made the calls shown in Listing 2.4 and Listing 2.5 and the system is about to begin a HotSync operation, it broadcasts the sysNotifySyncStartEvent notification to both clients.

The application is notified through the sysAppLaunchCmdNotify launch code. This launch code's parameter block is a SysNotifyParamType structure containing the notification name, the broadcaster, and a pointer to your specific data (myDataP in the example above). Some notifications contain extra information in a notifyDetailsP field in this structure. The HotSync notifications do not use the notifyDetailsP field.

The shared library is notified by a call to its SyncNotifyHandler function. This function is passed the same SysNotifyParamType structure that is passed through the launch code mechanism.


IMPORTANT: Because the callback pointer is used to directly call the function, the pointer must remain valid from the time SysNotifyRegister is called to the time the notification is broadcast. If the function is in a shared library, you must keep the library open. If the function is in a separately loaded code resource, the resource must remain locked while registered for the notification. When you close a library or unlock a resource, you must first unregister for any notifications. If you don't, the system will crash when the notification is broadcast.

Writing a Notification Handler ^TOP^

The application's response to sysAppLaunchCmdNotify and the shared library's callback function are called notification handlers. A notification handler may perform any processing necessary, including displaying a user interface or broadcasting other notifications.

When displaying a user interface, consider the possibility that you may be blocking other applications from receiving the notification. For this reason, it's generally not a good idea to display a modal form or do anything else that requires waiting for the user to respond. Also, many of the notifications are broadcast during SysHandleEvent, which means your application event loop may not have progressed to the point where it is possible for you to display a user interface, or that you may overflow the stack.

If you need to perform some lengthy process in a notification handler, one way to ensure that you aren't blocking other events is to send yourself a deferred notification. For example, Listing 2.6 shows a notification handler for the sysNotifyTimeChangeEvent notification that performs no work other than setting up a deferred notification (myDeferredNotifyEvent) and scheduling it for broadcast. When the application receives the myDeferredNotifyEvent, it calls the MyNotifyHandler function, which is where the application really handles the time change event.

Listing 2.6  Deferring notification within a handler


case sysAppLaunchCmdNotify :  
if (cmdPBP->notify->notifyType == sysNotifyTimeChangeEvent) { 
SysNotifyParamType notifyParm; 
MyGlobalsToAccess myData; 
 
/* initialize myData here */ 
 
/* Create the notification block. */ 
notifyParam.notifyType = myDeferredNotifyEvent; 
notifyParam.broadcaster = myCreatorID; 
notifyParam.notifyDetailsP= NULL; 
notifyParam.handled = false; 
 
/* Register for my notification */ 
SysNotifyRegister(myCardNo, appDBID, myDeferredNotifyEvent, NULL, 
sysNotifyNormalPriority, &myData); 
 
/* Broadcast the notification */ 
SysNotifyBroadcastDeferred(&notifyParam, NULL); 
 
} else if (cmdPBP->notify->notifyType == myDeferredNotifyEvent) 
MyNotifyHandler(cmdPBP->notify); 
break; 

The SysNotifyBroadcastDeferred function broadcasts the specified notification to all interested parties; however, it waits to do so until the current event has completed processing. Thus, by using a separate deferred notification, you can be sure that all other clients have had a chance to respond to the first notification.

There are several functions that broadcast notifications. Notification handlers should use SysNotifyBroadcastDeferred to avoid the possibility of overflowing the notification stack.

A special case of dealing with lengthy computations in a notification handler occurs when the system is being put to sleep. See "Sleep and Wake Notifications" below.

Sleep and Wake Notifications ^TOP^

Several notifications are broadcast at various stages when the system goes to sleep and when the system wakes up. These are:

These notifications are not guaranteed to be broadcast. For example, if the system goes to sleep because the user removes the batteries, sleep notifications are not sent. Thus, these notifications are unsuitable for applications where external hardware must be shut off to conserve power before the system goes to sleep.

If you want to know when the system is going to sleep because you have a small amount of cleanup that should occur beforehand, then register for sysNotifySleepNotifyEvent.

It is recommended that you not perform any sort of prolonged activity, such as displaying an alert panel that requests confirmation, in response to a sleep notification. If you do, the alert might be displayed long enough to trigger another auto-off event, which could be detrimental to other handlers of the sleep notify event.

In a few instances, you might need to prevent the system from going to sleep. For example, your code might be in the middle of performing some lengthy computation or in the middle of attempting a network connection. If so, register for the sysNotifySleepRequestEvent instead. This notification informs all clients that the system might go to sleep. If necessary, your handler can delay the sleep request by doing the following:

((SleepEventParamType *)
(notify->notifyDetailsP))->deferSleep++;

The system checks the deferSleep value when each notification handler returns. If it is nonzero, it cancels the sleep event.

After you defer sleep, your code is free to finish what it was doing. When it is finished, you must allow the system to continue with the sleep event. To do so, create a keyDownEvent with the resumeSleepChr and the command key bit set (to signal that the character is virtual) and add it to the event queue. When the system receives this event, it will again broadcast the sysNotifySleepRequestEvent to all clients. If deferSleep is 0 after all clients return, then the system knows it is safe to go to sleep, and it broadcasts the sysNotifySleepNotifyEvent to all of its clients.

Notice that you may potentially receive the sysNotifySleepRequestEvent many times before the system actually goes to sleep, but you receive the sysNotifySleepNotifyEvent exactly once.

During a wake-up event, the other two notifications listed above are broadcast. The sysNotifyEarlyWakeupEvent is broadcast very early on in the wakeup process, generally before the screen has turned on. At this stage, it is not guaranteed that the system will fully wake up. It may simply handle an alarm or a battery charger event and go back to sleep. Most applications that need notification of a wakeup event will probably want to register for sysNotifyLateWakeupEvent instead. At this stage, the screen has been turned on and the system is guaranteed to fully wake up.

When the handheld receives the sysNotifyLateWakeupEvent notification, it may be locked and waiting for the user to enter the password. If this is the case, you must wait for the user to unlock the handheld before you display a user interface. Therefore, if you intend to display a user interface when the handheld wakes up, you should make sure the handheld is not locked. If the handheld is locked, you should register for sysNotifyDeviceUnlocked notification and display your user interface when it is received. See Listing 2.7.

Listing 2.7  Responding to Late Wakeup Notification


case sysNotifyLateWakeupEvent:  
if ((Boolean)  
PrefGetPreference(prefDeviceLocked)) { 
SysNotifyRegister(myCardNo, myDbID,  
sysNotifyDeviceUnlocked, NULL,  
sysNotifyNormalPriority, NULL); 
} else { 
HandleDeviceWakeup(); 
} 
case sysNotifyDeviceUnlocked:  
HandleDeviceWakeup(); 

Helper Notifications ^TOP^

If the 4.0 New Feature Set is present, the helper notification, sysNotifyHelperEvent, is defined. This notification is a way for one application to request a service from another application. On Palm OS 4.0, the Dial application is the only application that performs a service through sysNotifyHelperEvent. Specifically, the Dial application dials a phone in response to this notification. The Address Book uses the Dial application to dial the phone number that the user has selected. You can use the Dial application in a similar way by broadcasting the sysNotifyHelperEvent from your application. You may also choose to write a provider of services.

In this section, the application that responds to the sysNotifyHelperEvent notification is called the helper, and the application that broadcasts the notification is called the broadcaster.

A helper registers for the sysNotifyHelperEvent notification. In the notification handler, the helper responds to action requests pertaining to the service that it provides.

Actions are requests to provide information about the service or to perform the service. The details structure for sysNotifyHelperEvent (a HelperNotifyEventType structure) defines three possible actions:

  • kHelperNotifyActionCodeEnumerate is a request for the helper to list the services that it can perform.
  • kHelperNotifyActionCodeValidate is a request for the helper to make sure that it can perform the service.
  • kHelperNotifyActionCodeExecute is a request to actually perform the service.

The possible services are defined in HelperServiceClass.h and described in the chapter "Helper API" of the Palm OS Programmer's API Reference. These services are to dial a number, email a message, send an SMS message, or send a fax. If you want to define your own service, you must register a unique creator ID for that service. Alternatively, you can use the creator ID of your application.

This section discusses the helper APIs, which include the sysNotifyHelperEvent notification and the data structures that it passes as the notifyDetailsP portion of the SysNotifyParamType structure. It covers:

When to Use the Helper API ^TOP^

If the 4.0 New Feature Set is present, there are several means by which one application can communicate with another application on the same handheld. Specifically, an application can send a launch code to another application (see "Launching Applications Programmatically" in this chapter), can use the Exchange Manager and Local Exchange Library to send data to another application (see the "Object Exchange" chapter), or can use the helper API to request that a service be performed. It can be difficult to determine which is the best method to use for your particular situation.

The helper API is best used in these circumstances:

  • The 4.0 New Feature Set is present.
  • You do not know anything about the receiving application.

    The helper API provides a means of communication where the sending and receiving application do not need to know anything about each other. This contrasts with the launch code mechanism, in which the sending application must know the card number and local ID of the receiving database as well as which launch code to send.

  • You want to communicate with any type of program.

    Because the helper API uses a notification, the helper can be a shared library or another separately loaded code resource. Launch codes can only be received by applications. Because the Exchange Manager works through launch codes, it also only works with applications.

Requesting a Helper Service ^TOP^

Listing 2.8 shows how an application should request the dial service. In general, you should do the following to request a service:

  • Broadcast a sysNotifyHelperEvent with a kHelperNotifyActionCodeValidate action each time you want to advertise that the service is available.

    For example, when the Address Book initializes the List view form, it checks to see if the dial service is available by broadcasting the notification with the action code kHelperNotifyActionCodeValidate. The Dial application makes sure the Telephony Library is open. If so, it sets handled to true in the SysNotifyParamType structure. If not, it sets handled to false. If handled is false after the notification is broadcast, the Address Book does not display the Dial menu item.

  • Broadcast a sysNotifyHelperEvent with a kHelperNotifyActionCodeExecute action when you want the service performed. See Listing 2.8.
  • If you want to obtain a list of all possible services, broadcast a sysNotifyHelperEvent with a kHelperNotifyActionCodeEnumerate action. You might do so when your application is launched, upon system reset, or any time the user performs a task where you might want to provide a service.

Listing 2.8  Requesting a helper service


Boolean PrvDialListDialSelected(FormType* frmP) { 
SysNotifyParamType param; 
HelperNotifyEventType details; 
HelperNotifyExecuteType execute; 
 
param.notifyType = sysNotifyHelperEvent; 
param.broadcaster = sysFileCAddress; 
param.notifyDetailsP = &details; 
param.handled = false; 
 
details.version = kHelperNotifyCurrentVersion; 
details.actionCode = kHelperNotifyActionCodeExecute; 
details.data.executeP = &execute; 
 
execute.serviceClassID = kHelperServiceClassIDVoiceDial; 
execute.helperAppID = 0; 
execute.dataP = FldGetTextPtr(ToolsGetFrmObjectPtr(frmP,  
DialListNumberField)); 
execute.displayedName = gDisplayName; 
execute.detailsP = 0; 
execute.err = errNone; 
 
SysNotifyBroadcast(&param); 
 
// Check error code 
if (!param.handled) 
// Not handled so exit the list - Unexpected error 
return true; 
else 
return (execute.err == errNone); 
} 

When you broadcast the sysNotifyHelperEvent, it's important to note the following:

  • Always use SysNotifyBroadcast, which broadcasts the notification synchronously.
  • The notification's notifyDetailsP parameter points to a HelperNotifyEventType. This structure allows the broadcaster to communicate with the helper.
  • The helper may allocate memory and add it to the HelperNotifyEventType structure. In particular, if the action code is kHelperNotifyActionCodeEnumerate, the helper allocates at least one structure of type HelperNotifyEnumerateListType and adds it to the data field in the HelperNotifyEventType structure. The broadcaster must free this memory, even though the helper allocated it.
  • The broadcaster uses the helperAppID field to communicate directly with a particular provider of the requested service. For example, suppose two applications provide a dial service. The broadcaster might discover these two applications through the enumerate action and then allow the user to specify which application should dial the phone number. When broadcasting the enumerate action, no helper ID is specified, so all helpers respond. After the user has set the preferred helper, the broadcaster sets the helperAppID field for the validate and execute actions to that helper's creator ID. A helper must check the helperAppID field and only respond to the notification if its creator ID matches the value in that field or if that field is 0.
  • The dataP field contains the data required to perform the service. For the dial service, dataP contains the phone number to dial. If any extra information is required or desired, then it is provided in the detailsP field. If you're requesting the email or SMS service, you use detailsP to provide the message to be sent. See the chapter "Helper API" of the Palm OS Programmer's API Reference for more information.
  • The handled field of SysNotifyParamType and the err field of the HelperNotifyEventType structure are used to return the result. Always set handled to false and err to errNone before broadcasting and check their values after the broadcast is complete. The helper uses handled to indicate if it attempted to handle the service. If handled is true, it uses err to indicate the success or failure of performing that service.

Implementing a Helper ^TOP^

To implement a helper, do the following:

  • Register to receive the sysNotifyHelperEvent. It is best to register for this notification in response to the sysAppLaunchCmdSyncNotify and sysAppLaunchCmdSystemReset launch codes. This registers your helper when it is first installed and re-registers it upon each system reset.
  • In the notification handler, handle the three possible actions: enumerate, execute, and validate. Note that even though the enumerate action is optional and not currently used by Address Book, a helper must respond to this action in its handler because another third party application might send the enumerate action.

Listing 2.9 and Listing 2.10 show how the Dial application responds to the enumerate and validate actions. Note that the enumerate action requires the helper to allocate memory and add that memory to the HelperNotifyEventType structure pointed to by notifyDetailsP in the SysNotifyParamType parameter block. In this case, the notifyDetailsP->dataP field is a linked list of HelperNotifyEnumerateListType structures. Each helper must allocate one of these structure per service and add it to the end of the list. The broadcaster is responsible for freeing all of these structures after the notification broadcast is complete.

Listing 2.9  Enumerating services provided


Boolean PrvAppEnumerate 
(HelperNotifyEventType*helperNotifyEventP) 
{ 
HelperNotifyEnumerateListType* newNodeP; 
MemHandle handle; 
MemPtr stringP; 
 
newNodeP = MemPtrNew 
(sizeof(HelperNotifyEnumerateListType)); 
 
// Get name to display in user interface.  
handle = DmGetResource(strRsc, HelperAppNameString); 
stringP = MemHandleLock(handle); 
StrCopy(newNodeP->helperAppName, stringP); 
MemHandleUnlock(handle); 
DmReleaseResource(handle); 
 
// Get name of service to display in UI.  
handle = DmGetResource(strRsc, HelperActionNameString); 
stringP = MemHandleLock(handle); 
StrCopy(newNodeP->actionName, stringP); 
MemHandleUnlock(handle); 
DmReleaseResource(handle); 
 
newNodeP->serviceClassID = kHelperServiceClassIDVoiceDial; 
newNodeP->helperAppID = kDialCreator; 
newNodeP->nextP = 0; 
 
// Add the new node. 
if (helperNotifyEventP->data.enumerateP == 0) { 
helperNotifyEventP->data.enumerateP = newNodeP; 
else { 
HelperNotifyEnumerateListType* nodeP; 
nodeP = helperNotifyEventP->data.enumerateP; 
//Look for the end of the list.  
while ( nodeP->nextP != 0 ) 
nodeP = nodeP->nextP; 
nodeP->nextP = newNodeP; 
} 
 
return true; 
} 

Listing 2.10 show how the Dial application responds to the validate action.

Listing 2.10  Responding to validate action


Boolean PrvAppValidate (SysNotifyParamType*sysNotifyParamP) 
{ 
HelperNotifyEventType* helperNotifyEvent; 
 
helperNotifyEvent = sysNotifyParamP->notifyDetailsP; 
// Check version 
if (helperNotifyEvent->version < 1) 
return false; 
 
// Check service 
if (helperNotifyEvent-> data.validateP->serviceClassID  
!= kHelperServiceClassIDVoiceDial) 
return false; 
 
// check appId (either null or me) 
if ((helperNotifyEvent->data.validateP->helperAppID != 0)  
&& (helperNotifyEvent->data.validateP->helperAppID !=  
kDialCreator)) 
return false; 
 
// Check Telephony library presence 
if (!PrvAppCheckTelephony()) 
return false; 
 
sysNotifyParamP->handled = true; 
return true; 
} 

When writing a helper, it is also important to note the following:

  • Always check the helperAppID field and only respond if it is 0 or if it matches your creator ID. For the validate and execute actions, a broadcaster may use helperAppID to only communicate with the desired helper.
  • If you handle the action, set handled to true. If the handling of the service was unsuccessful, set the err field in notifyDetailsP.
  • Always check the handled field before performing the service. If any helper can perform the service, you must make sure that the service has not already been performed before you perform it. If handled is true, the service has already been performed.
  • Remember that, as with all notifications, your notification handler does not have access to global variables. If there is data you need to access, pass it in the userDataP parameter to SysNotifyRegister. If you want to have the notification handler return before the service is fully complete, make a copy of any data in the parameter block that you will need to complete the service.

Socket Notifications ^TOP^

Socket notifications allow an application to receive a notification when certain conditions change in a network socket—such things as a remote socket being closed, or a socket receiving TCP data. Socket notifications are part of a larger topic known as socket notices. Please note that there is more to registering for socket notifications than merely calling SysNotifyRegister(). For details, see "Socket Notices," Palm OS Programmer's Companion, vol. II, Communications.

Launch Code Summary ^TOP^

Table 2.1 lists all Palm OS standard launch codes. These launch codes are declared in the header SystemMgr.h. All the parameters for a launch code are passed in a single parameter block, and the results are returned in the same parameter block.

Table 2.1  Palm OS Launch Codes

Code

Request

scptLaunchCmdExecuteCmd

Execute the specified Network login script plugin command.

scptLaunchCmdListCmds

Provide information about the commands that your Network script plugin executes.

sysAppLaunchCmdAddRecord

Add a record to a database.

sysAppLaunchCmdAlarmTriggered

Schedule next alarm or perform quick actions such as sounding alarm tones.

sysAppLaunchCmdAttention

Perform the action requested by the attention manager.

sysAppLaunchCmdCardLaunch

Launch the application. This launch code signifies that the application is being launched from an expansion card.

sysAppLaunchCmdCountryChange

Respond to country change.

sysAppLaunchCmdDisplayAlarm

Display specified alarm dialog or perform time-consuming alarm-related actions.

sysAppLaunchCmdExgAskUser

Let application override display of dialog asking user if they want to receive incoming data via the Exchange Manager.

sysAppLaunchCmdExgGetData

Notify application that it should send data using the Exchange Manager.

sysAppLaunchCmdExgPreview

Notify application that it should display a preview using the Exchange Manager.

sysAppLaunchCmdExgReceiveData

Notify application that it should receive incoming data using the Exchange Manager.

sysAppLaunchCmdFind

Find a text string.

sysAppLaunchCmdGoTo

Go to a particular record, display it, and optionally select the specified text.

sysAppLaunchCmdGoToURL

Launch an application and open a URL.

sysAppLaunchCmdHandleSyncCallApp

Perform some application-specific operation at the behest of the application's conduit.

sysAppLaunchCmdInitDatabase

Initialize database.

sysAppLaunchCmdLookup

Look up data. In contrast to sysAppLaunchCmdFind, a level of indirection is implied. For example, look up a phone number associated with a name.

sysAppLaunchCmdNormalLaunch

Launch normally.

sysAppLaunchCmdNotify

Broadcast a notification.

sysAppLaunchCmdOpenDB

Launch application and open a database.

sysAppLaunchCmdPanelCalledFromApp

Tell preferences panel that it was invoked from an application, not the Preferences application.

sysAppLaunchCmdReturnFromPanel

Tell an application that it's restarting after preferences panel had been called.

sysAppLaunchCmdSaveData

Save data. Often sent before find operations.

sysAppLaunchCmdSyncNotify

Notify applications that a HotSync has been completed.

sysAppLaunchCmdSystemLock

Sent to the Security application to request that the system be locked down.

sysAppLaunchCmdSystemReset

Respond to system reset. No UI is allowed during this launch code.

sysAppLaunchCmdTimeChange

Respond to system time change.

sysAppLaunchCmdURLParams

Launch an application with parameters from the Web Clipping Application Viewer.

Notification Summary ^TOP^

Table 2.2 lists all Palm OS standard notifications. These notifications are declared in the header NotifyMgr.h. All the parameters for a notification are passed in a SysNotifyParamType structure and the results are returned in that same structure.

Table 2.2  Notification Constants

Constant

Description

cncNotifyProfileEvent

The connection profile used by the Connection Panel has changed.

sysExternalConnectorAttachEvent

A device has been attached to an external connector.

sysExternalConnectorDetachEvent

A device has been detached from an external connector.

sysNotifyAntennaRaisedEvent

The antenna has been raised on a Palm VII series handheld.

sysNotifyCardInsertedEvent

An expansion card has been inserted into the expansion slot.

sysNotifyCardRemovedEvent

An expansion card has been removed from the expansion slot.

sysNotifyDBCreatedEvent

A database has been created.

sysNotifyDBChangedEvent

Database info has been set on a database, such as with DmSetDatabaseInfo.

sysNotifyDBDeletedEvent

A database has been deleted.

sysNotifyDBDirtyEvent

An overlay has been opened, a database has been opened for write, or another event has occurred which has made the database info "dirty."

sysNotifyDeleteProtectedEvent

The Launcher has attempted to delete a protected database.

sysNotifyDeviceUnlocked

The user has unlocked the handheld.

sysNotifyDisplayChangeEvent

The color table or bit depth has changed.

sysNotifyEarlyWakeupEvent

The system is starting to wake up.

sysNotifyForgotPasswordEvent

The user has tapped the Lost Password button in the Security application.

sysNotifyGotUsersAttention

The Attention Manager has informed the user of an event.

sysNotifyHelperEvent

An application has requested that a particular service be performed.

sysNotifyIrDASniffEvent

Not used.

sysNotifyLateWakeupEvent

The system has finished waking up.

sysNotifyLocaleChangedEvent

The system locale has changed.

sysNotifyMenuCmdBarOpenEvent

The system is about to display the menu command toolbar.

sysNotifyNetLibIFMediaEvent

The system has been connected to or disconnected from the network.

sysNotifyPhoneEvent

Reserved for future use.

sysNotifyPOSEMountEvent

System use only.

sysNotifyResetFinishedEvent

The system has finished a reset.

sysNotifyRetryEnqueueKey

The Attention Manager has failed to post a virtual character to the key queue.

sysNotifySleepNotifyEvent

The system is about to go to sleep.

sysNotifySleepRequestEvent

The system has decided to go to sleep.

sysNotifySyncFinishEvent

A HotSync operation has just completed.

sysNotifySyncStartEvent

A HotSync operation is about to begin.

sysNotifyTimeChangeEvent

The system time has just changed.

sysNotifyVolumeMountedEvent

A file system has been mounted.

sysNotifyVolumeUnmountedEvent

A file system has been unmounted.

Launch and Notification Function Summary ^TOP^