Previous Page Table of Contents Index Next Page

Palm OS Programmer's Companion


Palm Logo 8 Palm System Features

In this chapter, you learn how to work with the features that the Palm OS® system provides, such as sound, alarms, and floating-point operations. Most parts of the Palm OS are controlled by a manager, which is a group of functions that work together to implement a certain functionality. As a rule, all functions that belong to one manager use the same prefix and work together to implement a certain aspect of functionality.

This chapter discusses these topics:

Alarms

The Palm OS alarm manager provides support for setting real-time alarms, for performing some periodic activity, or for displaying a reminder. The alarm manager:

However, the alarm manager:

This section looks in some detail at how the alarm manager and applications interact when processing an alarm. It covers:

Setting an Alarm

The most common use of the alarm manager is to set a real-time alarm within an application. Often, you set this type of alarm because you want to inform the user of an event. For example, the Datebook application sets alarms to inform users of their appointments.

Implementing such an alarm is a two step process. First, use the function AlmSetAlarm to set the alarm. Specify when the alarm should trigger and which application should be informed at that time.

Listing 8.1 shows how the Datebook application sets an alarm.

Listing 8.1 Setting an alarm


static void SetTimeOfNextAlarm (UInt32 alarmTime, UInt32 ref)
{
    UInt16 cardNo;
    LocalID dbID;
    DmSearchStateType searchInfo;

    DmGetNextDatabaseByTypeCreator (true, &searchInfo,
    sysFileTApplication, sysFileCDatebook, true, &cardNo, &dbID);

    AlmSetAlarm (cardNo, dbID, ref, alarmTime, true);
}


Second, have your PilotMain function respond to the launch codes sysAppLaunchCmdAlarmTriggered and sysAppLaunchCmdDisplayAlarm.

When an alarm is triggered, the alarm manager notifies each application that set an alarm for that time via the sysAppLaunchCmdAlarmTriggered launch code. After each application has processed this launch code, the alarm manager sends each application sysAppLaunchCmdDisplayAlarm so that the application can display the alarm. The section "Alarm Scenario" gives more information about when these launch codes are received and what actions your application might take. For a specific example of responding to these launch codes, see the Datebook sample code.

It's important to note the following:

Alarm Scenario

Here's how an application and the alarm manager typically interact when processing an alarm:
The application sets an alarm using AlmSetAlarm. The alarm manager adds the new alarm to its alarm queue. The alarm queue contains all alarm requests. Triggered alarms are queued up until the alarm manager can send the launch code to the application that created the alarm. However, if the alarm queue becomes full, the oldest entry that has been both triggered and notified is deleted to make room for a new alarm. When the alarm time is reached, the alarm manager searches the alarm queue for the first application that set an alarm for this alarm time. The alarm manager sends this application the sysAppLaunchCmdAlarmTriggered launch code. The application can now:

The application should not perform any lengthy tasks in response to sysAppLaunchCmdAlarmTriggered because doing so will delay other applications from receiving alarms that are set to trigger at the same time. If this alarm requires no further processing, the application should set the purgeAlarm field in the launch code's parameter block to true before returning. Doing so removes the alarm from the queue, which means it won't receive the sysAppLaunchCmdDisplayAlarm launch code. The alarm manager finds in the alarm queue the next application that set an alarm and repeats steps 2 and 3. This process is repeated until no more applications are found with this alarm time. The alarm manager then finds once again the first application in the alarm queue who set an alarm for this alarm time and sends this application the launch code sysAppLaunchCmdDisplayAlarm. The application can now:

The alarm manager processes the alarm queue for the next application that set an alarm for the alarm being triggered and step 6 and 7 are repeated. This process is repeated until no more applications are found with this alarm time. If a new alarm time is triggered while an older alarm is still being displayed, all applications with alarms scheduled for this second alarm time are sent the sysAppLaunchCmdAlarmTriggered launch code, but the display cycle for the second set of alarms is postponed until all earlier alarms have finished displaying.

Setting a Procedure Alarm

Beginning with Palm OS version 3.2, the system supports setting procedure alarms in addition to the application-based alarms described in the previous sections. The differences between a procedure alarm and an application-based alarm are:

You might use procedure alarms if:

To set a procedure alarm, you call AlmSetProcAlarm instead of AlmSetAlarm. (Similarly, you use the AlmGetProcAlarm function instead of AlmGetAlarm to see if any alarms are pending for this procedure.)

AlmSetProcAlarm is currently implemented as a macro that calls AlmSetAlarm using a special value for the card number parameter to notify the alarm manager that this is a procedure alarm. Instead of specifying the application's local ID and card number, you specify a function pointer. The other rules for AlmSetAlarm still apply. Notably, a given function can only have one alarm pending at a time, and you can clear any pending alarm by passing 0 for the alarm time.

When the alarm triggers, the alarm manager calls the function you specified. The function should have the prototype:

void myAlarmFunc (UInt16 almProcCmd, SysAlarmTriggeredParamType *paramP)



IMPORTANT: The function pointer must remain valid from the time AlmSetProcAlarm is called to the time the alarm is triggered. If the procedure is in a shared library, you must keep the library open. If the procedure is in a separately loaded code resource, the resource must remain locked until the alarm fires. When you close a library or unlock a resource, you must remove any pending alarms. If you don't, the system will crash when the alarm is triggered.


The first parameter to your function specifies why the alarm manager has called the function. Currently, the alarm manager calls the function in two instances:

  • The alarm has triggered.

  • The user has changed the system time, so the alarm time should be adjusted.

The second parameter is the same structure that is passed with the sysAppLaunchCmdAlarmTriggered launch code. It provides access to the reference parameter specified when the alarm was set, the time specified when the alarm was set, and the purgeAlarm field, which specifies if the alarm should be removed from the queue. In the case of procedure alarms, the alarm should always be removed from the queue. The system sets the purgeAlarm value to true after calling your function.

Features

A feature is a 32-bit value that has special meaning to both the feature publisher and to users of that feature. Features can be published by the system or by applications.

Each feature is identified by a feature creator and a feature number:

Once a feature is published, it remains present until it is explicitly unregistered or the device is reset. A feature published by an application sticks around even after the application quits.

This section introduces the feature manager by discussing these topics:

The System Version Feature

An example for a feature is the system version. This feature is published by the system and contains a 32-bit representation of the system version. The system version has a feature creator of sysFtrCreator and a feature number of sysFtrNumROMVersion). Currently, the different versions of the system software have the following numbers:

0x01003001 Palm OS 1.0
0x02003000 Palm OS 2.0
0x03003000 Palm OS 3.0
0x03103000 Palm OS 3.1
0x03103000 Palm OS 3.1
0x03103000 Palm OS 3.1
0x03203000 Palm OS 3.2
0x03503000 Palm OS 3.5

Any application can find out the system version by looking for this feature. For example:

// See if we're on ROM version 2.0 or later.
FtrGet(sysFtrCreator, sysFtrNumROMVersion,
    &romVersion);
if (romVersion >= 0x02000000) {
    ....
}

Other system features are defined in SystemMgr.h. System features are stored in a feature table in the ROM. (In Palm OS 3.1 and higher, the contents of this table are copied into the RAM feature table at system startup.) Checking for the presence of system features allows an application to be compatible with multiple versions of the system by refining its behavior depending on which capabilities are present or not. Future hardware platforms may lack some capabilities present in the first platform, so checking the system version feature is important.



IMPORTANT: For best results, we recommend that you check for specific features rather than relying on the system version number to determine if a specific API is available. For more details on checking for features, see the appendix Compatibility Guide in Palm OS SDK Reference.


Application-Defined Features

Applications may find the feature manager useful for their own private use. For example, an application may want to publish a feature that contains a pointer to some private data it needs for processing launch codes. Because an application's global data is not generally available while it processes launch codes, using the feature manager is usually the easiest way for an application to get to its data.

The feature manager maintains one feature table in the RAM as well as the feature table in the ROM. Application-defined features are stored in the RAM feature table.

Using the Feature Manager

To check whether a particular feature is present, call FtrGet and pass it the feature creator and feature number. If the feature exists, FtrGet returns the 32-bit value of the feature. If the feature doesn't exist, an error code is returned.

To publish a new feature or change the value of an existing one, call FtrSet and pass the feature creator, number, and the 32-bit value of the feature. A published feature remains available until it is explicitly removed by a call to FtrUnregister or until the system resets; simply quitting an application doesn't remove a feature published by that application.

Call FtrUnregister to remove features that were created by calling FtrSet.

You can get a complete list of all published features by calling FtrGetByIndex repeatedly. Passing an index value starting at 0 to FtrGetByIndex and incrementing repeatedly by 1 eventually returns all available features. FtrGetByIndex accepts a parameter that specifies whether to search the ROM feature table or RAM feature table. Note that in Palm OS version 3.1 and higher, the contents of the ROM table are copied into the RAM table at system startup; thus the RAM table serves the entire system.

Feature Memory

Palm OS 3.1 adds support for feature memory. Feature memory provides quick, efficient access to data that persists between invocations of an application. The values stored in feature memory persist until the device is reset or until you explicitly free the memory. Feature memory is memory allocated from the storage heap. Thus, you write to feature memory using DmWrite, which means that writing to feature memory is no faster than writing to a database. However, feature memory can provide more efficient access to that data in certain circumstances.

To allocate a chunk of feature memory, call FtrPtrNew, specifying a feature creator, a feature number, the number of bytes to allocate, and a location where the feature manager can return a pointer to the newly allocated memory chunk. For example:

FtrPtrNew(appCreator,
myFtrMemFtr, 32, &ftrMem);

Elsewhere in your application, you can obtain the pointer to the feature memory chunk using FtrGet.

NOTE:  

Starting with Palm OS 3.5 FtrPtrNew allows allocating chunks larger than 64k. Do keep in mind standard issues with allocating large chunks of memory: there might not be enough contiguous space, and it can impact system performance.

Feature memory is considered a performance optimization. The conditions under which you'd use it are not common, and you probably won't find them in a typical application. You use feature memory in code that:

  • Is executed infrequently

  • Does not have access to global variables

  • Needs access to data whose contents change infrequently and that cannot be stored in a 32-bit feature value

For example, suppose you've written a function that is called in response to a launch code, and you expect to receive this launch code frequently. Suppose that function needs access to the application's preferences database. At the start of the function, you'd need to open the database and read the data from it. If the function is called frequently, opening the database each time can be a drain on performance. Instead, you can allocate a chunk of feature memory and write the values you need to that chunk. Because the chunk persists until the device is reset, you only need to open the database once. Listing 8.2 illustrates this example.

Listing 8.2 Using feature memory


MyAppPreferencesType prefs;

if (FtrGet(appCreator, myPrefFtr, (UInt32*)&prefs) != 0) {

    // Feature memory doesn't exist, so allocate it.
    FtrPtrNew(appCreator, myPrefFtr, 32, &thePref);

    // Load the preferences database.
    PrefGetAppPreferences (appCreator, prefID, &prefs,
    sizeof(prefs), true);

    // Write it to feature memory.
    DmWrite(thePref, 0, &prefs, sizeof(prefs));
}
// Now prefs is guaranteed to be defined.


Another potential use of feature memory is to "publish" data from your application or library to other applications when that data doesn't fit in a normal 32-bit feature value. For example, suppose you are writing a communications library and you want to publish an icon that client applications can use to draw the current connection state. The library can use FtrPtrNew to allocate a feature memory chunk and store an icon representing the current state in that location. Applications can then use FtrGet to access the icon and pass the result to WinDrawBitmap to display the connection state on the screen.

Notifications

On systems where the Notification Feature Set is present, your application can receive notifications 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:

The Palm OS system and the built-in applications send notifications when certain events occur. See the chapter "Notification Manager" in the Palm OS SDK Reference for a complete list. (The notification manager broadcasts the notifications and maintains a list of clients for each notification).

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.

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

Registering for a Notification

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 8.3.

Listing 8.3 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 8.4.

Listing 8.4 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:

After you've made the calls shown in Listing 8.3 and Listing 8.4 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

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 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 8.5 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 8.5 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 two functions that broadcast notifications: SysNotifyBroadcast, which immediately broadcasts the notification, and SysNotifyBroadcastDeferred, which waits until the next time EvtGetEvent is called. 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

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:

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.

Sound

The Palm Computing platform device has primitive sound generation. A square wave is generated directly from the 68328's PWM circuitry. There is frequency, duration, and volume control. Additionally, Palm OS 3.0 and higher support creating and playing standard MIDI sounds.

The Palm OS sound manager provides an extendable API for playing custom sounds and system sounds, and for controlling default sound settings. Although the sound API accommodates multichannel design, the system provides only a single sound channel at present.

The sound hardware can play only one simple tone at a time through an onboard piezoelectric speaker. Note that for a particular amplitude level, the Palm III device is slightly louder than its predecessors.

Single tones can be played by the SndDoCmd function and system sounds are played by the SndPlaySystemSound function. The end-user can control the amplitude of alarm sounds, game sounds, and system sounds by means of the Preferences application. System-supplied sounds include the Information, Warning, Error, Startup, Alarm, Confirmation, and Click sounds.

Palm OS 3.0 introduces support for Standard MIDI Files (SMFs), format 0. An SMF is a note-by-note description of a tune--Palm OS doesn't support sampled sound, multiple voices, or complex "instruments." You can download the SMF format specification from the http://www.midi.org Web site.

The alarm sounds used in the built-in Date Book application are SMFs stored in the System MIDI Sounds database and can be played by the SndPlaySmf function.

All SMF records in the System MIDI Sounds database are available to the user. Developers can add their own alarm SMFs to this database as a way to add variety and personalization to their devices. You can use the sysFileTMidi file type and sysFileCSystem creator to open this database.

Each record in the database is a single SMF, with a header structure containing the user-visible name. The record includes a song header, then a track header, followed by any number of events. The system only recognizes the keyDown, keyUp and tempo events in a single track; other commands which might be in the SMF are ignored. For more information, see the following:

You can use standard MIDI tools to create SMF blocks on desktop computers, or you can write code to create them on the Palm OS device. The sample code project "RockMusic," particularly the routines in the MakeSMF.c file, can be helpful to see how to create an SMF programmatically.

Previous versions of Palm OS don't support SMFs or asynchronous notes; don't use the new routines or commands when the FtrGet function returns a system version of less than 0x03000000. Doing so will crash your application. See the section "The System Version Feature" for more information.

Synchronous and Asynchronous Sound

The SndDoCmd function executes synchronously or asynchronously according to the operation it is to perform. The sndCmdNoteOn and sndCmdFrqOn operations execute asynchronously; that is, they are non-blocking and can be interrupted by another sound command. In contrast, the sndCmdFreqDurationAmp operation is synchronous and blocking (it cannot be interrupted).

The SndPlaySmf function is also synchronous and blocking; however, the Sound Manager polls the key queue periodically during playback and halts playback in progress if it finds events generated by user interaction with the screen, digitizer, or hardware-based buttons. Optionally, the caller can override this default behavior to specify that the SndPlaySmf function play the SMF to completion without being interrupted by user events.

Using the Sound Manager

Before playing custom sounds that require a volume (amplitude) setting, your code needs to discover the user's current volume settings. To do so in Palm OS 3.X, pass one of the prefSysSoundVolume, prefGameSoundVolume, or prefAlarmSoundVolume selectors to the PrefGetPreference function.

NOTE:  

See "Sound Preferences Compatibility Information" for important information regarding the correct use of sound preferences in various versions of Palm OS.

You can pass the returned amplitude information to the SndPlaySmf function as one element of a SndSmfOptionsType parameter block. Alternatively, you can pass amplitude information to the SndDoCmd function as an element of a SndCommandType parameter block.

To execute a sound manager command, pass to the SndDoCmd function a sound channel pointer (presently, only NULL is supported and maps to the shared channel), a pointer to a structure of SndCommandType, and a flag indicating whether the command should be performed asynchronously.

To play SMFs, call the SndPlaySMF function. This function, which is new in Palm OS 3.0, is used by the built in Date Book application to play alarm sounds.

To play single notes, you can use either of the SndPlaySMF or SndDoCmd functions. Of course, you can use the SndPlaySMF function to play a single MIDI note from an SMF. You can also use the SndDoCmd function to play a single MIDI note by passing the sndCmdNoteOn command selector to this function. To specify by frequency the note to be played, pass the sndCmdFrqOn command selector to the SndDoCmd function.You can pass the sndCmdQuiet selector to this function to stop playback of the current note.

The system provides no specialized API for playing game sounds or alarm sounds. When an alarm triggers, the application that set the alarm must use the standard Sound Manager API to play the sound associated with that alarm. Similarly, game sounds are implemented by the game developer using any appropriate element of the Sound Manager API. Games should observe the prefGameSoundVolume setting, as described in the section "Sound Preferences Compatibility Information."

To play a default system sound, such as a click or an error beep, pass the appropriate system sound ID to the SndPlaySystemSound function, which will play that sound at the volume level specified by the user's system sound preference. For the complete list of system sound IDs, see the SoundMgr.h file provided by the Palm OS SDK.

Adding a Standard MIDI File to a Database

To add a format 0 standard MIDI file to the system MIDI database, you can use code similar to the AddSmfToDatabase example function shown in the following code listing. This function returns 0 if successful, and returns a non-zero value otherwise. To use a different database, pass different creator and type values to the DmOpenDatabaseByTypeCreator function.

Listing 8.6 AddSmfToDatabase


// Useful structure field offset macro
#define prvFieldOffset(type, field) ((UInt32)(&((type*)0)->field))

// returns 0 for success, nonzero for error
Int16 AddSmfToDatabase(MemHandle smfH, Char* trackName)
{
    Err err = 0;
    DmOpenRef dbP;
    UInt16* recIndex;
    MemHandle recH;
    UInt8* recP;
    UInt8* smfP;
    UInt32 bMidiOffset;
    UInt32 dwSmfSize;
    SndMidiRecHdrType recHdr;
   
    bMidiOffset = sizeof(SndMidiRecHdrType) +
StrLen(trackName) + 1;

    dwSmfSize = MemHandleSize(smfH);
   
    recHdr.signature = sndMidiRecSignature;
    recHdr.reserved = 0;
    recHdr.bDataOffset = bMidiOffset;

    dbP = DmOpenDatabaseByTypeCreator(sysFileTMidi, sysFileCSystem,
dmModeReadWrite | dmModeExclusive);

    if (!dbP)
    return 1;

    // Allocate a new record for the midi resource
    recIndex = dmMaxRecordIndex;
    recH = DmNewRecord(dbP, &recIndex, dwSmfSize + bMidiOffset);
    if ( !recH )
    return 2;
   
    // Lock down the source SMF and target record and copy the data
    smfP = MemHandleLock(smfH);
    recP = MemHandleLock(recH);
   
    err = DmWrite(recP, 0, &recHdr, sizeof(recHdr));
    if (!err) err = DmStrCopy(recP, prvFieldOffset(SndMidiRecType,
name), trackName);

    if (!err) err = DmWrite(recP, bMidiOffset, smfP, dwSmfSize);
   
    // Unlock the pointers
    MemHandleUnlock(smfH);
    MemHandleUnlock(recH);
   
    //Because DmNewRecord marks the new record as busy,
// we must call DmReleaseRecord before closing the database

    DmReleaseRecord(dbP, recIndex, 1);

DmCloseDatabase(dbP);


    return err;
}


Saving References to Standard MIDI Files

To save a reference to a SMF stored in a particular database, save its record ID and the name of the database in which it is stored. Do not store the database ID between invocations of your application, because various events, such as a HotSync, can invalidate database IDs. Using an invalid database ID can crash your application.

Retrieving a Standard MIDI File From a Database

Standard MIDI Files (SMFs) are stored as individual records in a MIDI record database--one SMF per record. Palm OS defines the database type sysFileTMidi for MIDI record databases. The system MIDI database, with type sysFileTMidi and creator sysFileCSystem, holds multiple system alarm sounds. In addition, your applications can create their own private MIDI databases of type sysFileTMidi and your own creator.

To obtain a particular SMF, you need to identify the database in which it resides and the specific database record which holds the SMF data. The database record itself is always identified by record ID. The MIDI database in which it resides may be identified by name or by database ID. If you know the creator of the SMF, you can use the SndCreateMidiList utility function to retrieve this information. Alternatively, you can use the Data Manager record API functions to iterate through MIDI database records manually in search of this information.

The SndCreateMidiList utility function retrieves information about Standard Midi Files from one or more MIDI databases. This information is returned as a table of entries. Each entry contains the name of an SMF; its unique record ID; and the database ID and card number of the record database in which it resides.

Once you have the appropriate identifiers for the record and the database in which it resides, you need to open the MIDI database. If you have identified the database by type and creator, pass the sysFileTMidi type and an appropriate creator value to the DmOpenDatabaseByTypeCreator function. For example, to retrieve a SMF from the system MIDI database, pass type sysFileTMidi and creator sysFileCSystem. The DmOpenDatabaseByTypeCreator function returns a reference to the open database.

If you have identified the database by name, rather than by creator, you'll need to discover its database ID in order to open it. The DmFindDatabase function returns the database ID for a database specified by name and card number. You can pass the returned ID to the DmOpenDatabase function to open the database and obtain a reference to it.

Once you have opened the MIDI database, call DmFindRecordByID to get the index of the SMF record. To retrieve the record itself, pass this index value to either of the functions DmQueryRecord or DmGetRecord. When you intend to modify the record, use the DmGetRecord function--it marks the record as busy. When you intend to use the record in read-only fashion, use the DmQueryRecord function --it does not mark the record as busy. You must lock the handle returned by either of these functions before making further use of it.

To lock the database record's handle, pass it to the MemHandleLock function, which returns a pointer to the locked record holding the SMF data. You can pass this pointer to the SndPlaySmf function in the smfP parameter to play the MIDI file.

When you've finished using the record, unlock the pointer to it by calling the MemPtrUnlock function. If you've used DmGetRecord to open the record for editing, you must call DmReleaseRecord to make the record available once again to other callers. If you used DmQueryRecord to open the record for read-only use, you need not call DmReleaseRecord.

Finally, close the database by calling the DmCloseDatabase function.

Sound Preferences Compatibility Information

The sound preferences implementation and API varies slightly among versions 1.0, 2.0, and 3.X of Palm OS. This section describes how to use sound preferences correctly for various versions of Palm OS.

Because versions 2.0 and 3.X of Palm OS provide backward compatibility with previous sound preference mechanisms, applications written for an earlier version of the sound preferences API will get correct sound preference information from newer versions of Palm OS. However, it is strongly recommended that new applications use the latest API.

Using Sound Preferences on All Palm OS Devices

Because the user chooses sound preference settings, your application should respect them and adhere to their values. Further, you should always treat sound preferences as read-only values.

At reset time, the sound manager reads stored preference values and caches them for use at run time. The user interface controls update both the stored preference values and the sound manager's cached values.

The PrefSetPreference function writes to stored preference values without affecting cached values. New values are read at the next system reset. The system-use-only SndSetDefaultVolume function updates cached values but not stored preferences. Applications should avoid modifying stored preferences or cached values in favor of respecting the user's choices for preferences.

Using Palm OS 1.0 Sound Preferences

To read sound preference values in version 1.0 of Palm OS, call the PrefGetPreferences function to obtain the data structure shown in Listing 8.7. This SystemPreferencesTypeV10 structure holds the current values of all system-wide preferences. You must extract from this structure the values of the sysSoundLevel and alarmSoundLevel fields. These values are the only sound preference information that Palm OS version 1.0 provides.

Each of these fields holds a value of either slOn (on) or slOff (off). Your code must interpret the values read from these fields as an indication of whether those volumes should be on or off, then map them to appropriate amplitude values to pass to Sound Manager functions: map the slOn selector to the sndMaxAmp constant (defined in SoundMgr.h) and map the slOff selector to the value 0 (zero).

Listing 8.7 SystemPreferencesTypeV10 data structure


typedef struct {
    UInt16 version; // Version of preference info
   
    // International preferences
    CountryType country; // Country the device is in
    DateFormatType dateFormat; // Format to display date in
    DateFormatType longDateFormat; // Format to display date in
    UInt8 weekStartDay; // Sunday or Monday
    TimeFormatType timeFormat; // Format to display time in
    NumberFormatType numberFormat; // Format to display numbers in
   
    // system preferences
    UInt8 autoOffDuration; // Time period before shutting off
    SoundLevelTypeV20 sysSoundLevel; // error beeps
    SoundLevelTypeV20 alarmSoundLevel; // alarm only
    Boolean hideSecretRecords; // True to not display records with
    // their secret bit attribute set
    Boolean deviceLocked; // Device locked until the system
    // password is entered
    UInt16 sysPrefFlags; // Miscellaneous system pref flags copied into
// the global GSysPrefFlags at boot time.

    SysBatteryKind sysBatteryKind;
// The type of batteries installed.
// This is copied into the globals
// GSysbatteryKind at boot time.

   
    } SystemPreferencesTypeV10;


Using Palm OS 2.0 Sound Preferences

Version 2.0 of Palm OS introduces a new API for retrieving individual preference values from the system. You can pass any of the selectors prefSysSoundLevelV20, prefGameSoundLevelV20, or prefAlarmSoundLevelV20 to the PrefGetPreference function to retrieve individual amplitude preference values for alarm sounds, game sounds, or for overall (system) sound amplitude. As in Palm OS 1.0, each of these settings holds values of either slOn (on) or slOff (off), as defined in the Preferences.h file. Your code must interpret the values read from these fields as an indication of whether those volumes should be on or off, then map them to appropriate amplitude values to pass to Sound Manager functions: map the slOn selector to the sndMaxAmp constant (defined in SoundMgr.h file) and map the slOff selector to the value 0 (zero).

For a complete listing of selectors you can pass to the PrefGetPreference function, see the Preferences.h file.

Using Palm OS 3.X Sound Preferences

Palm OS version 3.X enhances the resolution of sound preference settings by providing discrete amplitude levels for games, alarms, and the system overall. As usual, do not set preferences yourself, but treat them as read-only values indicating the proper volume level for your application to use.

Palm OS 3.X defines the new sound amplitude selectors prefSysSoundVolume, prefGameSoundVolume, and prefAlarmSoundVolume for use with the PrefGetPreference function. The values this function returns for these selectors are actual amplitude settings that may be passed directly to Sound Manager functions.

NOTE:  

The amplitude selectors used in previous versions of Palm OS (all ending with the Level suffix, such as prefGameSoundLevel) are obsoleted in version 3.0 of Palm OS and replaced by new selectors. The old selectors remain available in Palm OS 3.X to ensure backward compatibility and are suffixed V20 (for example, prefGameSoundLevelV20).

Ensuring Sound Preferences Compatibility

For greatest compatibility with multiple versions of the sound preferences mechanism, your application should condition its sound preference code according to the version of Palm OS on which it is running. See "The System Version Feature" for more information.

When your application is launched, it should retrieve the system version number and save the results in its global variables (or equivalent structure) for use elsewhere. If the major version number is 3 (three) or greater, then use the 3.0 mechanism for obtaining sound amplitude preferences, since this reflects the user's selection most accurately. If the major version number is 2 (two), then use the 2.0 mechanism described in "Using Palm OS 2.0 Sound Preferences." If it is 1 (one), then use the 1.0 mechanism described in "Using Palm OS 1.0 Sound Preferences."

Avoid calling new APIs (including new selectors) when running on older versions of Palm OS that do not implement them. In particular, note that violating any of the following conditions will cause your application to crash:

System Boot and Reset

Any reset is normally performed by sticking a bent-open paper clip or a large embroidery needle into the small hole in the back of the device. This hole, known as the "reset switch" is above and to the right of the serial number sticker (on Palm III devices). Depending on additional keys held down, the reset behavior varies, as follows:

Soft Reset

A soft reset clears all of the dynamic heap (Heap 0, Card 0). The storage heaps remain untouched. The operating system restarts from scratch with a new stack, new global variables, restarted drivers, and a reset communication port. All applications on the device receive a sysAppLaunchCmdSystemReset launch code.

Soft Reset + Up Arrow

Holding the up-arrow down while pressing the reset switch with a paper clip causes the same soft reset logic with the following two exceptions:

Hard Reset

A hard reset is performed by pressing the reset switch with a paper clip while holding down the power key. This has all the effects of the soft reset. In addition, the storage heaps are erased. As a result, all programs, data, patches, user information, etc. are lost. A confirmation message is displayed asking the user to confirm the deletion of all data.

The sysAppLaunchCmdSystemReset launch code is sent to the applications at this time. If the user selected the "Delete all data" option, the digitizer calibration screen comes up first. The default databases for the four main applications is copied out of the ROM.

If you hold down the up arrow key when the "Delete all data" message is displayed, and then press the other four application buttons while still holding the up arrow key, the system is booted without reading the default databases for the four main applications out of ROM.

System Reset Calls

The system manager provides support for booting the Palm OS device. It calls SysReset to reset the device. This call does a soft reset and has the same effect as pressing the reset switch on the unit. Normally applications should not use this call.

SysReset is used, for example, by the Sync application. When the user copies an extension onto the Palm OS device, the Sync application automatically resets the device after the sync is completed to allow the extension to install itself.

The SysColdBoot call is similar, but even more dangerous. It performs a hard reset that clears all user storage RAM on the device, destroying all user data.

Hardware Interaction

Palm OS differs from a traditional desktop system in that it's never really turned off. Power is constantly supplied to essential subsystems and the on/off key is merely a way of bringing the device in or out of low-power mode. The obvious effect of pressing the on/off key is that the LCD turns on or off. When the user presses the power key to turn the device off, the LCD is disabled, which makes it appear as if power to the entire unit is turned off. In fact, the memory system, real-time clock, and the interrupt generation circuitry are still running, though they are consuming little current.

This section looks at Palm OS power management, discussing the following topics:

Palm OS Power Modes

To minimize power consumption, the operating system dynamically switches between three different modes of operation: sleep mode, doze mode, and running mode. The system manager controls transitions between different power modes and provides an API for controlling some aspects of the power management.

To maximize battery life, the processor on the Palm Computing platform device is kept out of running mode as much as possible. Any interrupt generated on the device must therefore be capable of "waking" up the processor. The processor can receive interrupts from the serial port, the hard buttons on the case, the button on the cradle, the programmable timer, the memory module slot, the real-time clock (for alarms), the low-battery detector, and any built-in peripherals such as a pager or modem.

Guidelines for Application Developers

Normally, applications don't need to be aware of power management except for a few simple guidelines. When an application calls EvtGetEvent to ask the system for the next event to process, the system automatically puts itself into doze mode until there is an event to process. As long as an application uses EvtGetEvent, power management occurs automatically. If there has been no user input for the amount of time determined by the current setting of the auto-off preference, the system automatically enters sleep mode without intervention from the application.

Applications should avoid providing their own delay loops. Instead, they should use SysTaskDelay, which puts the system into doze mode during the delay to conserve as much power as possible. If an application needs to perform periodic work, it can pass a time out to EvtGetEvent; this forces the unit to wake up out of doze mode and to return to the application when the time out expires, even if there is no event to process. Using these mechanisms provides the longest possible battery life.

Power Management Calls

The system calls SysSleep to put itself immediately into low-power sleep mode. Normally, the system puts itself to sleep when there has been no user activity for the minimum auto-off time or when the user presses the power key.

The SysSetAutoOffTime routine changes the auto-off time value. This routine is normally used by the system only during boot, and by the Preferences application. The Preferences application saves the user preference for the auto-off time in a preferences database, and the system initializes the auto-off time to the value saved in the preferences database during boot. While the auto-off feature can be disabled entirely by calling SysSetAutoOffTime with a time-out of 0, doing this depletes the battery.

The current battery level and other information can be obtained through the SysBatteryInfo routine. This call returns information about the battery, including the current battery voltage in hundredths of a volt, the warning thresholds for the low-battery alerts, the battery type, and whether external power is applied to the unit. This call can also change the battery warning thresholds and battery type.

The Microkernel

Palm OS has a preemptive multitasking kernel that provides basic task management.

Most applications don't need the microkernel services because they are handled automatically by the system. This functionality is provided mainly for internal use by the system software or for certain special purpose applications.

In this version of the Palm OS, there is only one user interface application running at a time. The User Interface Application Shell (UIAS) is responsible for managing the current user-interface application. The UIAS launches the current user-interface application as a subroutine and doesn't get control back until that application quits. When control returns to the UIAS, the UIAS immediately launches the next application as another subroutine. See "Power Management Calls" for more information.

Usually, the UIAS is the only task running. Occasionally though, an application launches another task as a part of its normal operation. One example of this is the Sync application, which launches a second task to handle the serial communication with the desktop. The Sync application creates a second task dedicated to the serial communication and gives this task a lower priority than the main user-interface task. The result is optimal performance over the serial port without a delay in response to the user-interface controls.

Normally, there is no user interaction during a sync, so that the serial communication task gets all of the processor's time. However, if the user does tap on the screen, for example, to cancel the sync, the user-interface task immediately processes the tap, since it has a higher priority. Alternatively, the Sync application could have been written to use just one task, but then it would have to periodically poll for user input during the serial communication, which would hamper performance and user-interface response time.

NOTE:  

Only system software can launch a separate task. The multi-tasking API is not available to developer applications.

Retrieving the ROM Serial Number

Some Palm devices, beginning with the Palm III product, hold a 12-digit serial number that identifies the device uniquely. (Earlier devices do not have this identifier.) The serial number is held in a displayable text buffer with no null terminator. The user can view the serial number in the Application Launcher application. (The pop-up version of the Launcher does not display the serial number.) The Application Launcher also displays to the user a checksum digit that you can use to validate user entry of the serial number.

To retrieve the ROM serial number programmatically, pass the sysROMTokenSnum selector to the SysGetROMToken function. If the SysGetROMToken function returns an error, or if the returned pointer to the buffer is NULL, or if the first byte of the text buffer is 0xFF, then no serial number is available.

The DrawSerialNumOrMessage function shown in Listing 8.8 retrieves the ROM serial number, calculates the checksum, and draws both on the screen at a specified location. If the device has no serial number, this function draws a message you specify. This function accepts as its input a pair of coordinates at which it draws output, and a pointer to the message it draws when a serial number is not available.

Listing 8.8 DrawSerialNumOrMessage


static void DrawSerialNumOrMessage(Int16 x, Int16 y, Char* noNumberMessage)
{
Char* bufP;
UInt16* bufLen;
Err retval;
Int16 count;
UInt8 checkSum;
Char checksumStr[2];
// holds the dash and the checksum digit


retval = SysGetROMToken (0, sysROMTokenSnum,
(UInt8**) &bufP, &bufLen);

if ((!retval) && (bufP) && ((UInt8) *bufP != 0xFF)) {
// there's a valid serial number!

    // Calculate the checksum: Start with zero, add each digit,
// then rotate the result one bit to the left and repeat.

checkSum = 0;
for (count=0; count<bufLen; count++) {
checkSum += bufP[count];
checkSum = (checkSum<<1) | ((checkSum & 0x80) >> 7);
}
    // Add the two hex digits (nibbles) together, +2
// (range: 2 - 31 ==> 2-9, A-W)

    // By adding 2 to the result before converting to ascii,
// we eliminate the numbers 0 and 1, which can be
// difficult to distinguish from the letters O and I.
checkSum = ((checkSum>>4) & 0x0F) + (checkSum & 0x0F) + 2;


    // draw the serial number and find out how wide it was
WinDrawChars(bufP, bufLen, x, y);
x += FntCharsWidth(bufP, bufLen);


    // draw the dash and the checksum digit right after it
checksumStr[0] = '-';
checksumStr[1] =
((checkSum < 10) ? (checkSum +'0'):(checkSum -10 +'A'));
WinDrawChars (checksumStr, 2, x, y);
}

else // there's no serial number
// draw a status message if the caller provided one
if (noNumberMessage)
WinDrawChars(noNumberMessage, StrLen(noNumberMessage),x, y);

}


Time

The Palm Computing platform device has a real-time clock and programmable timer as part of the 68328 processor. The real-time clock maintains the current time even when the system is in sleep mode (turned off). It's capable of generating an interrupt to wake the device when an alarm is set by the user. The programmable timer is used to generate the system tick count interrupts (100 times/second) while the processor is in doze or running mode. The system tick interrupts are required for periodic activity such as polling the digitizer for user input, key debouncing, etc.

The date and time manager (called time manager in this chapter) provides access to both the 1-second and 0.01-second timing resources on the Palm OS device.

The basic time-manager API provides support for setting and getting the real-time clock in seconds and for getting the current system ticks value (but not for setting it). The system manager provides more advanced functionality for setting up a timer task that executes periodically or in a given number of system ticks.

This section discusses the following topics:

Using Real-Time Clock Functions

The real-time clock functions of the time manager include TimSetSeconds and TimGetSeconds. Real time on the Palm OS device is measured in seconds from midnight, Jan. 1, 1904. Call TimSecondsToDateTime and TimDateTimeToSeconds to convert between seconds and a structure specifying year, month, day, hour, minute, and second.

Using System Ticks Functions

The Palm OS device maintains a tick count that starts at 0 when the device is reset. This tick increments

For tick-based timing purposes, applications should use the macro SysTicksPerSecond, which is conditionally compiled for different platforms. Use the function TimGetTicks to read the current tick count.

Although the TimGetTicks function could be used in a loop to implement a delay, it is recommended that applications use the SysTaskDelay function instead. The SysTaskDelay function automatically puts the unit into low-power mode during the delay. Using TimGetTicks in a loop consumes much more current.

Floating-Point

Palm OS 1.0 provided 16-bit floating point arithmetic. Instead of using standard mathematical symbols, you called functions like FplAdd, FplSub, and so on.

Palm OS 2.0 and later implements floating point arithmetic differently than Palm OS 1.0 did. The floating-point library in OS versions 2.0 and later provides 32-bit and 64-bit floating point arithmetic.

Using Floating Point Arithmetic

To take advantage of the floating-point library, applications can now use the mathematical symbols + - * /instead of using functions like FplAdd, FplSub, etc.

When compiling the application, you have to link in the floating point library under certain circumstances. Choose from one of these options:

There are control panel settings in the IDE which let you select the appropriate floating-point model.

Floating-point functionality is identical in either method.

Using 1.0 Floating-Point Functionality

The original Fpl calls (documented in the chapter "Float Manager" in the Palm OS SDK Reference) are still available. They may be useful for applications that don't need high precision, don't want to incur the size penalty of the float library, and want to run on 1.0 devices only. To get 1.0 behavior, use the 1.0 calls (FplAdd, etc.) and don't link in the library.

Summary of System Features

Alarm Manager Functions
AlmSetAlarm
AlmSetProcAlarm
AlmGetAlarm
AlmGetProcAlarm

Feature Manager Functions
FtrGet
FtrSet
FtrPtrNew
FtrPtrResize
FtrGetByIndex
FtrUnregister
FtrPtrFree

Notification Manager Functions
SysNotifyRegister
SysNotifyBroadcast
SysNotifyUnregister
SysNotifyBroadcastDeferred

Sound Manager Functions
SndCreateMidiList
SndGetDefaultVolume
SndPlaySystemSound
SndDoCmd
SndPlaySmf
SndPlaySmfResource

System Manager Functions
Launching Applications
SysAppLaunch
SysBroadcastActionCode
SysUIAppSwitch
System Dialogs
SysGraffitiReferenceDialog
SysKeyboardDialogV10
SysKeyboardDialog
Power Management
SysBatteryInfo
SysSetAutoOffTime
SysBatteryInfoV20
SysTaskDelay
System Management
SysLibFind
SysRandom
SysGremlins
SysLibLoad
SysReset
Working With Strings and Resources
SysBinarySearch
SysQSort
SysCreatePanelList
SysFormPointerArrayToStrings
SysInsertionSort
SysCopyStringResource
SysStringByIndex
Database Support
SysCreateDataBaseList SysCurAppDatabase
Error Handling
SysErrString
Event Handling
SysHandleEvent
System Information
SysGetOSVersionString
SysGetROMToken
SysGetStackInfo
SysTicksPerSecond

Time Manager Functions
Allowing User to Change Date and Time
DayHandleEvent
SelectDay
SelectTimeV33
SelectDayV10
Changing the Date
DateAdjust
TimSetSeconds
TimAdjust
Converting to Date Format
DateDaysToDate
TimSecondsToDateTime
DateSecondsToDate
Converting Dates to Other Formats
DateToAscii
DateToDays
TimGetSeconds
TimGetTicks
TimeToAscii
DateToDOWDMFormat
TimDateTimeToSeconds
Date Information
DayOfMonth
DaysInMonth
DayOfWeek

Float Manager Functions
FplAdd
FplBase10Info
FplFloatToLong
FplFree
FplInit
FplMul
FplAToF
FplDiv
FplFloatToULong
FplFToA
FplLongToFloat
FplSub



Palm OS Programmer's Companion

  Previous Page Table of Contents Index Next Page  

This is page 77 of 85 in this book

Palm Computing Platform Development Zone
Copyright © 2000, Palm, Inc. All rights reserved.