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 a Palm Powered™ device. 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 by the global find facility. Palm OS applications don't have an Exit command; instead they exit when a user switches to another application.
This chapter describes how an application launches, how an application stops, and the code you must write to perform these tasks properly. This chapter covers:
Launch Codes and Launching an Application
Responding to Launch Codes
Launching Applications Programmatically
Stopping an Application
Launch Code Summary
Application Manager Function Summary
This chapter does not cover the main application event loop. The event loop is covered in Chapter 3, "Events and the Event Loop."
Launch Codes and Launching an Application
An application launches when its PilotMain()
function is called with 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. 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 launching 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 launch-code-specific 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" for a list of standard Palm OS launch flags.
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 fully described.
Responding to Launch Codes
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, returning errNone
. Otherwise, it performs the action immediately and returns.
Listing 2.1 shows selected parts of PilotMain()
from the Datebook application as an example.
Listing 2.1 Parts of Datebook's PilotMain() function
uint32_t PilotMain (uint16_t cmd, MemPtr cmdPBP, uint16_t launchFlags) { ExgPassableSocketType* passableSocketP; ExgSocketType* socketP; DmOpenRef dbP; uint32_t cursorID = dbInvalidCursorID; uint32_t value; uint32_t defaultForm; uint16_t mode; Boolean launched; status_t error = errNone; // the error returned by PilotMain // Get application dbP if ((error = SysGetModuleDatabase(SysGetRefNum(), &gApplicationDbID, &gApplicationDbP)) < errNone) return error; // Assign the local device time zone gettimezone(gDeviceTimeZone, TZNAME_MAX); switch (cmd){ // Launch code sent by the launcher or the datebook button. case sysAppLaunchCmdNormalLaunch: error = PrvStartApplication (); if (error < errNone) return (error); // If the user previously left the Datebook while viewing the agenda, // return there. Otherwise, go to the day view error = FtrGet (sysFileCDatebook, recentFormFeature, &value); if (error) defaultForm = defaultRecentForm; else defaultForm = value; FrmGotoForm(gApplicationDbP, (uint16_t) defaultForm); PrvEventLoop (); PrvStopApplication (); break; case sysAppLaunchCmdExgGetData: // Handle a get request ... case appLaunchCmdExgGetFullLaunch: ... case appLaunchCmdAlarmEventGoto: // This action code is a DateBook specific custom launch code. // It will always require that the app launches as it is a result // of a SysUIAppSwitch call. ... case sysAppLaunchCmdGoTo: // This action code might be sent to the app when it's already running // if the use hits the "Go To" button in the Find Results dialog box. launched = launchFlags & sysAppLaunchFlagNewGlobals; if (launched) { // New start error = PrvStartApplication (); if (error < errNone) break; PrvGoToItem ((GoToParamsPtr) cmdPBP, launched); PrvEventLoop (); PrvStopApplication (); } else // application was already started PrvGoToItem ((GoToParamsPtr) cmdPBP, launched); break; case sysAppLaunchCmdFind: // Launch code sent when the user is looking for some text. ... case sysAppLaunchCmdSyncNotify: // Launch code sent by sync application to notify the datebook // application that its database was been synced. ... case sysAppLaunchCmdNotify: ... case sysAppLaunchCmdSystemReset: // This action code is sent after the system is reset. ... case sysAppLaunchCmdExgAskUser: ... case sysAppLaunchCmdExgReceiveData: // Receive the record. The app will parse the data and add it // to the database. ... case sysAppLaunchCmdExgPreview: ... case sysAppLaunchCmdInitDatabase: // This action code is sent by the DesktopLink server when it creates // a new database. We will initialize the new database. ... case sysAppLaunchCmdAlarmTriggered: // Launch code sent by Alarm Manager to notify the datebook // application that an alarm has triggered. ... case sysAppLaunchCmdAttention: // Launch Code sent by Attention Manager to let Datebook draw // alarmed events. ... case sysAppLaunchCmdExportRecordGetCount: ... case sysAppLaunchCmdExportRecord: ... case sysAppLaunchCmdImportRecord: ... case sysAppLaunchCmdDeleteRecord: ... default: break; } return error; }
NOTE: The above code calls
SysGetModuleDatabase()
and gettimezone()
for every launch code. Programs should only call functions like these for those launch codes in which the values are needed: both of these calls result in an IPC (interprocess communcation), which should be avoided whenever possible.
Responding to Normal Launch
When an application receives the launch code sysAppLaunchCmdNormalLaunch
, it begins with a start routine, then goes into an event loop, and finally exits with a stop routine. (The event loop is described in Chapter 3, "Events and the Event Loop." The stop routine is shown in the section "Stopping an Application" at the end of this chapter.)
During the start routine, your application should perform these actions:
- 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.
- Find the application database. If none exists, create it and initialize it.
- Get application-specific preferences and initialize related global variables.
- Initialize any other global variables.
As you saw in Listing 2.1, the Datebook application responds to sysAppLaunchCmdNormalLaunch
by calling a function named PrvStartApplication()
. Listing 2.2 shows this function.
Listing 2.2 PrvStartApplication() from Datebook
static status_t PrvStartApplication(void) { status_t err; uint16_t mode; DateTimeType dateTime; DatebookPreferenceType prefs; int16_t prefsVersion; time_t rangeStartTime; time_t rangeEndTime; // Load the ToDo application as a shared lib. Doing this, the ToDo // globals will be kept all along the Datebook execution err = SysLoadModule(sysFileTApplication, sysFileCToDo, 0, 0, &ToDoRefNum); ErrNonFatalDisplayIf(err < errNone, "Unable to load the ToDo application as a shared library"); // Determime if secret record should be shown. PrivateRecordVisualStatus = CurrentRecordVisualStatus = (privateRecordViewEnum)PrefGetPreference(prefShowPrivateRecords); mode = (PrivateRecordVisualStatus == hidePrivateRecords) ? dmModeReadWrite : (dmModeReadWrite | dmModeShowSecret); // Get the time formats from the system preferences. TimeFormat = (TimeFormatType)PrefGetPreference(prefTimeFormat); // Get the date formats from the system preferences. LongDateFormat = (DateFormatType)PrefGetPreference(prefLongDateFormat); ShortDateFormat = (DateFormatType)PrefGetPreference(prefDateFormat); // Get the starting day of the week from the system preferences. StartDayOfWeek = (uint16_t) PrefGetPreference(prefWeekStartDay); // Get today's date. TimSecondsToDateTime (TimGetSeconds(), &dateTime); Date.year = dateTime.year - firstYear; Date.month = dateTime.month; Date.day = dateTime.day; // Find the application's data file. If it don't exist create it. err = DateDBOpenDatabase (&ApptDB, mode); if (err < errNone) return err; // Create initial cursor based on current date and a 1-day range (day / agenda view) CalculateStartEndRangeTimes(&Date, 1, &rangeStartTime, &rangeEndTime, NULL); err = ApptDBOpenOrRequeryWithNewRange(ApptDB, &gApptCursorID, rangeStartTime, rangeEndTime, true); if (err < errNone) return err; PIMAppProfilingBegin("PrvStartApplication, TimeZoneToAscii") // Get the devivce localized time zone name TimeZoneToAscii(gDeviceTimeZone, gLocalizedTimeZomeName); PIMAppProfilingEnd(); // Read the preferences / saved-state information prefsVersion = DatebookLoadPrefs (&prefs); DayStartHour = prefs.dayStartHour; DayEndHour = prefs.dayEndHour; AlarmPreset = prefs.alarmPreset; SaveBackup = prefs.saveBackup; ShowTimeBars = prefs.showTimeBars; CompressDayView = prefs.compressDayView; ShowTimedAppts = prefs.showTimedAppts; ShowUntimedAppts = prefs.showUntimedAppts; ShowDailyRepeatingAppts = prefs.showDailyRepeatingAppts; AlarmSoundRepeatCount = prefs.alarmSoundRepeatCount; AlarmSoundRepeatInterval = prefs.alarmSoundRepeatInterval; AlarmSoundUniqueRecID = prefs.alarmSoundUniqueRecID; ApptDescFont = prefs.apptDescFont; AlarmSnooze = prefs.alarmSnooze; // Get the previous current category PrvLoadCurrentCategories(&DateBkCurrentCategoriesCount, &DateBkCurrentCategoriesP); // Reset selection TopVisibleAppt = 0; // Set initial active tab for the details dialog in day view DetailsSetDefaultEventDetailsTab(DetailsBookOptionsTabId); return errNone; }
Responding to Other Launch Codes
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.
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
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: There are other ways for applications to communicate. See "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 one of the SysAppLaunch...()
functions from the Application Manager. You use these functions when you want to make use of another application's functionality and eventually return control to your application. The process of calling another application as a subroutine is sometimes referred to as a sublaunch.
The Application Manager defines the following SysAppLaunch...()
functions:
-
SysAppLaunch()
- Launches an application as a subroutine of the caller in the caller's process. This function can only be called from the main UI thread. Use with care: most applications will want to use one of the other
SysAppLaunch...()
functions instead. -
SysAppLaunchLocal()
- Launch an application as a subroutine of the caller in the caller's process, unless the application being launched is already running in another process. In this case, the launch code and parameters are sent to the running application. This function can only be called from the main UI thread.
-
SysAppLaunchRemote()
- Launch an application as a subroutine of the caller in a separate, newly-created process, unless the application being launched is already running in another process in which case the launch code and parameters are sent to the running application. Remote launching allows applications to execute untrusted code without compromising their own security. This function can only be called from the main UI thread.
NOTE: The parameter block you pass in to any of the above cannot contain pointers to other data or objects.
For example, you could use SysAppLaunchLocal()
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.
An alternative, simpler method of sending launch codes is the SysBroadcastActionCode()
call. This function automatically finds all other user-interface applications and calls the appropriate SysAppLaunch...()
function to send the launch code to each of them.
When an application is launched using one of the SysAppLaunch...()
functions, 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 SysGetModuleDatabase()
to get the database ID of its own database.
If you want to actually close your application and open another application, use SysUIAppSwitch()
instead. This function notifies the system which application to launch next and feeds an appStopEvent
event into the event queue. If and when the current application responds to the quit event and returns, the system launches the new application.
WARNING! Do not use the
SysUIAppSwitch()
or SysAppLaunch...()
functions to open the Application Launcher application. If another application has replaced the default launcher with one of its own, this function will open the system-supplied launcher instead of the custom one. To open the correct Launcher reliably, enqueue a keyDownEvent
that contains a launchChr
.
When you launch an application using SysUIAppSwitch()
you have the option to pass a parameter block (using the cmdPBP
parameter) containing application-specific information to the application being launched. To create this parameter block, allocate a block of memory using MemPtrNew()
and then call MemPtrSetOwner()
to set the block's owner ID to 0. This assigns ownership of the block to the system; memory blocks owned by the system aren't automatically freed when the calling application exits. Once ownership of the block has been assigned to the system, neither the launching nor the launched application need worry about freeing the block since the operating system will do this itself after the launched application exits.
Note that your parameter block must be self contained. That is, it must not have pointers to anything on the stack or to memory blocks that are owned by an application. If you don't need to pass a parameter block to the application being launched, pass NULL
for the cmdPBP
parameter.
Sublaunching in Another Process
Each sublaunch takes place in its own transient process, except when the currently running application receives a request to sublaunch itself, in which case the sublaunch takes place in the Application Process. The sublaunched thread, whether in the main Application process or a sublaunched process, effectively becomes the Application process and thread for the duration of the sublaunch. In other words, the thread requesting the sublaunch is effectively suspended while the sublaunched thread executes. Once the sublaunched application exits, the sublaunched process that was created to accommodate the sublaunch is then completely torn down.
See "Processes and Applications" of Exploring Palm OS: System Management for a diagram showing all of the Palm OS Cobalt processes.
Creating Your Own Launch Codes
Palm OS contains a large number of predefined launch codes, which are listed in "Launch Code Summary." 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 an unsigned 16-bit value. All launch codes with values 0–32767 are reserved for use by the system and for future enhancements. Launch codes beginning at sysAppLaunchCmdCustomBase (that is, those from 32768 to 65535) are available for private use by applications.
Stopping an Application
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. (Events are covered in detail in Chapter 3, "Palm OS Events.") Applications typically call a "StopApplication" function in response to the appStopEvent
, before returning from PilotMain()
.
The appStopEvent
gives the a application an opportunity to perform cleanup activities including closing databases and saving state information. In the stop function, an application should first flush all active records, close the application's database, and save those aspects of the current state needed for the next time the application is started. Listing 2.3 is an example of a stop function—this is from the Datebook application.
Listing 2.3 PrvStopApplication() from Datebook
static void PrvStopApplication (void) { // Save the preferences DatebookSavePrefs(); // Save current categories PrvSaveCurrentCategories(DateBkCurrentCategoriesCount, DateBkCurrentCategoriesP); // Send a frmSave event to all the open forms. FrmSaveAllForms (); // Close all the open forms. FrmCloseAllForms (); // Close the application's cursor ApptCloseCursor(&gApptCursorID); // Close the application's data file. DbCloseDatabase (ApptDB); ApptDB = NULL; // Unload the ToDo application loaded as a shared library if (ToDoRefNum != kRALInvalidRefNum) SysUnloadModule(ToDoRefNum); }
Launch Code Summary
The following tables list all Palm OS standard launch codes. These launch codes are declared in CmnLaunchCodes.h
, TelephonyLib.h
, and Preferences.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
Schedule next alarm or perform quick actions such as sounding alarm tones. |
|
Sent to the executable module that is launched in a background thread. |
|
Launch the application. This launch code signifies that the application is being launched from an expansion card. |
|
Instructs the application to delete a specified database record. |
|
Display specified alarm dialog or perform time-consuming alarm-related actions. |
|
Instructs the application to export a specified database record. |
|
Instructs the application to return the number of records in the application's database. |
|
Indicates a failure in an application that was just switched to. |
|
Instructs the application's start-up code to de-initialize the process's UI. |
|
Go to a particular record, display it, and optionally select the specified text. |
|
Perform some application-specific operation at the behest of the application's conduit. |
|
Presents the application with a record to be added to or updated in the application's database. |
|
Instructs the application's start-up code to initialize the process's UI. |
|
Look up data. In contrast to |
|
Tell preferences panel that it was invoked from an application, not the Preferences application. |
|
Sent to an application that is launched as a pinlet instead of |
|
Tell an application that it's restarting after preferences panel had been called. |
|
Sent to the Security application to request that the system be locked down. |
|
Respond to system reset. No UI is allowed during this launch code. |
|
Informs the boot application that a no-notify reset has occurred. This launch code is for system use only. |
|
An application has exited from its |
|
Informs operating system initialization procedures that the system is booting. This launch code is for system use only. |
|
An executable module is being unloaded; gives the module a last chance to do any needed "de-initialization." |
|
Retrieve a pointer to an executable module's globals structure. |
|
Retrieve an executable module's module ID. This launch code is for system use only. |
|
An executable module has been loaded; gives the executable a chance to do any needed initialization. |
|
A newly-loaded executable module should initialize its module ID and linker stub. This launch code is for system use only. |
|
Determine if a shared library can be called from a 68K application. |
|
A package has been loaded and should supply an image context used by the package to determine when the package should be unloaded. |
|
Asks a package for the function used to instantiate the package's components. |
|
Informs a patch that a target shared library has been unloaded. |
|
Informs a patch that one of the shared libraries it wants to patch is being loaded. |
|
Requests pointers to the functions used by the Pen Input Manager when interacting with a pinlet. |
|
Get a "quick edit" label for one of the standard service panels. |
|
Table 2.2 Communications-related Launch Codes