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

3    Events and the Event Loop

Programming Basics

Exploring Palm OS®

This chapter discusses events—the primary mechanism by which the operating system communicates with an application—and the event loop that forms the heart of all Palm OS® applications. The topics covered are:

Palm OS Events
The Structure of an Event
The Application Event Loop
Using Events to Communicate Between Threads
Palm OS-Generated Events
Summary of Event APIs

When working with events, you use APIs declared in Event.h. These APIs are documented in Chapter 7, "Event,"; additional event-related APIs are documented in Chapter 13, "System Event Manager." The event codes representing the various events are primarily declared in EventCodes.h, documented in Chapter 8, "Event Codes." Many events are only of interest to developers working with a particular technology; accordingly, those events have been documented in the corresponding Exploring Palm OS volumes. See "Palm OS-Generated Events" for a complete list of all events organized according to the book, and thus the technology, with which it is most commonly associated.

Palm OS Events ^TOP^

Palm OS applications are event-driven: user actions and some system requests are placed in an event queue from which the events can be retrieved and acted upon. Each thread running in either the Application or Background process can have its own event queue into which events destined for that thread are placed (but note that the event queue is part of the UI context of the thread, and a UI context is relatively heavyweight, so unless you need it you shouldn't create the UI context). The operating system places into each queue only those events that are relevant for that queue.

Certain events—such as pen and key events—are classified as low-level events. Applications rarely work with low-level events directly; the operating system translates low-level events into higher-level events that are then posted to the appropriate event queue. Applications that do work with low-level events directly might do so to enqueue key events or to retrieve each of the pen points that comprise a pen stroke.

Because Palm OS translates low-level pen and key events into higher-level events, most Palm OS events can be ignored by the typical application. For instance, if the user taps an on-screen button a low-level penDownEvent is generated. This event is passed on to the control object, which then posts a ctlEnterEvent. Then as the user moves the pen, a series of penMoveEvents are posted. Finally,when the user lifts the pen a penUpEvent is posted. If the pen was lifted within the control, the operating system realizes that the user just tapped an on-screen button and posts a ctlSelectEvent to the event queue. Of these events, an application that is only interested in knowing when the user taps an on-screen button need only watch for the ctlSelectEvent; it can ignore the other events entirely.

The Structure of an Event ^TOP^

The EventType structure describes an event. It consists of three main parts:

  • A 32-bit value that identifies the event that has taken place.
  • A standard data block that, for many events, contains the state of the pen at the time the event occurred.
  • An optional event-specific data block.

The EventType structure looks like this (with the event-specific data structures that comprise the data union omitted for clarity):

typedef struct EventType {
   eventsEnum eType;
   Boolean penDown;
   uint8_t padding_1;
   uint16_t padding_2;
   uint32_t tapCount;
   Coord screenX;
   Coord screenY;
   union {
      ...
   } data;
} EventType;

In this structure the data union is used only by those events that have additional data associated with them. For instance, a keyDownEvent's data field contains the following structure:

struct _KeyDownEventType {
  wchar_t chr;
  uint16_t keyCode;
  uint16_t modifiers;
} keyDown

On the other hand, an appStopEvent—an indication to the application that it should stop—needs no additional data.

The Application Event Loop ^TOP^

Upon receiving a sysAppLaunchCmdNormalLaunch launch code, a typical Palm OS application does the following:

  1. Perform any needed application-specific initialization.
  2. Display the application's main form.
  3. Enter a loop, retrieving and handling events until an appStopEvent is retrieved. This part of the program is known as the application event loop.
  4. Perform any necessary cleanup, and exit.

In the event loop, the application fetches events from the queue and dispatches them, taking advantage of the default system functionality as appropriate. Most events are passed on to the system, which knows how to handle them. For example, the system knows how to respond to pen taps on forms or menus.

The application typically remains in the event loop until the system tells it to shut itself down by sending an appStopEvent through the event queue. The application must detect this event and terminate.

Listing 3.1 shows a typical application event loop. Figure 3.1 graphically illustrates this same event loop, with additional explanation for each of the steps.

Listing 3.1  Sample application event loop


static void AppEventLoop(void){ 
   status_t error; 
   EventType event; 
 
   do { 
      EvtGetEvent(&event, evtWaitForever); 
 
      if (SysHandleEvent(&event)) 
         continue; 
 
      if (MenuHandleEvent(0, &event, &error)) 
         continue; 
 
      if (AppHandleEvent(&event)) 
         continue; 
 
      FrmDispatchEvent(&event); 
 
   } while (event.eType != appStopEvent); 
} 

Figure 3.1  Control flow in a typical application

As illustrated both in the code and the flowchart, within the basic application event loop the application performs the following steps (Each of which is discussed in greater detail in the following sections):

  1. Fetch an event from the event queue.
  2. Call SysHandleEvent() to give the system an opportunity to handle the event.
  3. If SysHandleEvent() did not completely handle the event, the application gives the menu system a chance to handle it by calling MenuHandleEvent().
  4. If MenuHandleEvent() did not completely handle the event, your application now gets a chance to deal with it. ApplicationHandleEvent() is a function your application has to provide. Your ApplicationHandleEvent() function typically only handles the frmLoadEvents, since each form typically handles its own events.
  5. If ApplicationHandleEvent did not completely handle the event, the application calls FrmDispatchEvent(). This function sends the event to the active form's event handler and, if the form's event handler didn't deal with it, FrmDispatchEvent() passes it off to the operating system to do any default processing of the event.

Notice how the event flow allows your application to rely on system functionality as much as it wants. If your application wants to know whether a button is pressed, it has only to wait for a ctlSelectEvent. All the details of the event queue are handled by the system.

Some events are actually requests for the application to do something, for example, frmOpenEvent. Typically, all the application does is initialize the elements on the form and then wait for events it can handle to arrive from the queue.

Retrieving Events ^TOP^

Applications call EvtGetEvent() to obtain the next available event from the current thread's event queue. Pass a pointer to an EventType structure into which the event will be copied, and a timeout value indicating the length of time the function should wait for an event if there are no events currently on the queue. In most instances you simply pass evtWaitForever, indicating that EvtGetEvent() shouldn't return until there is at least one event in the queue.

EvtGetEvent() has no return value: it always returns a valid event. If there are no events in the queue and the specified timeout period elapses, EvtGetEvent() generates and returns a nilEvent.

If your application needs to perform a lengthy process, such as a data transfer during a communications session, it should periodically call EvtGetEvent(). That is, you call EvtGetEvent() and do work when you get a nilEvent. Each time you get a nilEvent, do a chunk of work, but be sure to continue to call EvtGetEvent() frequently (like every half second), so that pen taps and other events get noticed quickly. Note that in situations like these you'll probably also want to display a progress dialog. For more information on progress dialogs, see Exploring Palm OS: User Interface.

Handling System Events ^TOP^

The system handles events like power on/power off, Graffiti® 2 input, tapping inputarea icons; these events are not posted to your thread's event queue. Other events, like the pressing of the "hard" buttons on the device, are posted to your thread's event queue to give your application the opportunity to handle them. Those events that your application does not handle entirely by itself should be passed on to the operating system; you do this by calling SysHandleEvent().

SysHandleEvent() returns true if the event was completely handled and no further processing of the event is required. The application is then free to pick up the next event from the queue.

Handling Menu Events ^TOP^

MenuHandleEvent() handles two types of events:

  • If the user has tapped in the area that invokes a menu, MenuHandleEvent() brings up the menu.
  • If the user has tapped inside a menu to invoke a menu command, MenuHandleEvent() removes the menu from the screen and puts the events that result from the command onto the event queue.

MenuHandleEvent() returns true if the event was completely handled.

Handling Form Load Events: the AppHandleEvent() Function ^TOP^

Your ApplicationHandleEvent() function typically only handles the frmLoadEvents, since each form typically handles its own events. The EventType structure that comprises a frmLoadEvent contains both the formID of the form to be loaded and a DmOpenRef to the open resource database that contains the form. Using this information, your AppHandleEvent() function should do the following:

  1. Load and initialize the form. This is most commonly done by calling FrmInitForm(). At this point the form has not yet been drawn, nor is it active.
  2. Make the form the active form. Call FrmSetActiveForm() to do this. The active form receives all key and pen input, and all drawing is performed on the active form (until otherwise specified).
  3. Set the form's event handler using FrmSetEventHandler(). You supply the address of a callback function that will receive all events intended for the form. Generally, you create a separate callback function for each form in your application.
  4. Call FrmInitLayout() to prepare the form for automatic resizing. This last step is optional, but recommended: you need only call this function if your form should be automatically resized in response to a winResizedEvent.

Your AppHandleEvent() function should return true if it handled the event, or false if the event should be passed on to be handled elsewhere.

Listing 3.2 illustrates a typical AppHandleEvent() function.

Listing 3.2  A sample AppHandleEvent() function


static Boolean AppHandleEvent(EventType* pEvent) { 
   uint16_t formId; 
   FormType *pForm; 
 
   if (pEvent->eType == frmLoadEvent) { 
      // Load the form resource. 
      formId = pEvent->data.frmLoad.formID; 
       
      pForm = FrmInitForm(gAppDB, formId); 
      FrmSetActiveForm(pForm); 
 
      // Set the event handler for the form.  The handler of 
      // the currently active form is called by 
      // FrmHandleEvent each time is receives an event. 
      switch (formId) { 
         case MainForm: 
            FrmSetEventHandler(pForm, MainFormHandleEvent); 
            FrmInitLayout(pForm, gMainFormLayout); 
            break; 
 
         case Form2Form: 
            FrmSetEventHandler(pForm, Form2FormHandleEvent); 
            FrmInitLayout(pForm, gForm2FormLayout); 
            break; 
 
         default: 
            ErrFatalDisplay("Invalid Form Load Event"); 
            break; 
      } 
       
      return true; 
   } 
    
   return false; 
} 

Handling Form-Specific Events ^TOP^

FrmDispatchEvent() begins by sending the event to the application's event handler for the active form. This is the event handler routine that was established in ApplicationHandleEvent(). This gives the application's code the first opportunity to process events that pertain to the current form. The application's event handler may completely handle the event and return true, in which case FrmDispatchEvent() returns to the application's event loop. Otherwise, FrmDispatchEvent() calls FrmHandleEvent() to provide the system's default processing for the event. In many cases this default handling is sufficient; see the documentation for FrmHandleEvent() for an explanation of how that function deals with various events.

In the process of handling an event, an application may have to first close the current form and then open another one. This happens as follows:

  1. The application calls FrmGotoForm() to bring up another form. FrmGotoForm() enqueues a frmCloseEvent for the currently active form, and then enqueues a frmLoadEvent and a frmOpenEvent for the new form.
  2. When the application gets the frmCloseEvent, it closes and erases the currently active form.
  3. When the application gets the frmLoadEvent, it loads and then activates the new form. Normally, the form remains active until it's closed. (Note that this wouldn't work if you preload all forms, but pre-loading is really discouraged. Applications don't need to be concerned with the overhead of loading forms; loading is so fast that applications can do it when they need it.) The application's event handler for the new form is also established.
  4. Upon receipt of the frmOpenEvent the application performs any required initialization of the form.
  5. Upon receipt of a frmUpdateEvent the application draws the form on the display.

After FrmGotoForm() has been called, any further events that make their way to the application event loop's FrmDispatchEvent() call are dispatched to the event handler for the form that's currently active—the form specified in the FrmGotoForm() call.

Using Events to Communicate Between Threads ^TOP^


NOTE: This section describes how the Palm OS event mechanism can be used to facilitate communications between separate threads of execution. For more complete information on writing multi-tasking Palm OS applications, see Exploring Palm OS: System Management.

Each thread can have its own event queue. While the Palm OS event mechanism is most commonly used to keep applications apprised of user actions, you can also employ it to communicate with other threads, either in the same or a different process.

Communicating Between Threads in a Single Process ^TOP^

Given a handle to an event queue, you can post an arbitrary event to that queue by calling EvtAddEventToEventQueue() or EvtAddUniqueEventToEventQueue(). Note that the former is different from EvtAddEventToQueue(), which always posts events to the default queue for the current thread.

To obtain a handle to the event queue of another thread in the same process you simply call EvtGetThreadEventQueue() from within that thread. When you are done with the queue be sure to call EvtReleaseEventQueue().

Communicating Between Threads in Different Processes ^TOP^

As when posting events to the queue of another thread in the same process, you use EvtAddEventToEventQueue() or EvtAddUniqueEventToEventQueue() to post events to a queue in a separate process. How you obtain the handle to the other thread's queue, however, differs. The mechanism you use depends on whether you created the other thread yourself or whether you need to attach to an already-running thread in another process:

  • If you are creating the thread yourself, you'll likely use EvtCreateBackgroundThread(). This function returns a handle to the newly-created background thread's event queue. Note that although threads don't necessarily have an event queue—you need to call WinStartThreadUI() to create a UI context (and thus an event queue) for a thread created with SysThreadCreate()—background threads created with EvtCreateBackgroundThread() do always have an event queue.
  • To obtain a handle to the event queue of an already-running thread in another process, that thread must have published its queue by name using EvtPublishEventQueue(). Then, as long as the posting thread knows the name by which the queue was published, it need only call EvtLookupEventQueue() to obtain the queue handle.

Two-way communication is enabled by the replyQueue parameter to the EvtAddEventToEventQueue() call: if task A supplies a handle to its own queue when posting an event to task B's event queue, task B can send a reply by posting an event back to task A's queue. In order to obtain the handle to task A's event queue, task B must call EvtGetReplyEventQueue() while processing the original event posted by task A. Note that the reply queue is associated with a single event posting; this allows a background server task to service multiple clients at the same time.

Palm OS-Generated Events ^TOP^

The following is complete list of all events generated by Palm OS that are of interest to developers. Because most events are generated by or handled by specific areas of the system, they are documented in other books in the Exploring Palm OS series, as listed in the following tables.

Palm OS-Generated Events

General Events

appStopEvent

nilEvent

Events documented in Exploring Palm OS: Input Services

gsiStateChangeEvent

keyDownEvent

keyHoldEvent

keyHoldEvent5

keyUpEvent

keyUpEvent5

penDownEvent

penMoveEvent

penUpEvent

Events documented in Exploring Palm OS: User Interface

ctlEnterEvent

ctlExitEvent

ctlRepeatEvent

ctlSelectEvent

daySelectEvent

fldChangedEvent

fldEnterEvent

fldHeightChangedEvent

frmCloseEvent

frmGadgetEnterEvent

frmGadgetMiscEvent

frmGotoEvent

frmLoadEvent

frmOpenEvent

frmSaveEvent

frmScrollPrvRefreshEvent

frmStopDialogEvent

frmTitleEnterEvent

frmTitleSelectEvent

frmUpdateEvent

insertionPointOffEvent

insertionPointOnEvent

lstEnterEvent

lstExitEvent

lstSelectEvent

menuCloseEvent

menuCmdBarOpenEvent

menuCmdBarTimeoutEvent

menuEvent

menuOpenEvent

popSelectEvent

prgUpdateEvent

sclEnterEvent

sclExitEvent

sclRepeatEvent

sysClearUIEvent

tblEnterEvent

tblExitEvent

tblSelectEvent

winEnterEvent

winExitEvent

winFocusGainedEvent

winFocusLostEvent

winResizedEvent

winUpdateEvent

winVisibilityChangedEvent

Events to be documented in Exploring Palm OS: Creating a FEP

tsmConfirmEvent

tsmFepButtonEvent

tsmFepChangeEvent

tsmFepDisplayOptionsEvent

tsmFepModeEvent

tsmFepSelectOptionEvent

Events documented in Exploring Palm OS: Telephony and SMS

telAsyncReplyEvent (kTelTelephonyEvent)

Events used internally by the operating system or reserved for future use

amWorkerDoneEvent

attnIndicatorEnterEvent

attnIndicatorSelectEvent

certMgrWorkerDoneEvent

debugEvent

exgLocalEvtNotify

exgLocalEvtDie

reservedEventCode1

reservedEventCode2

reservedEventCode3

reservedFindEvent

stringInputEvent

tunneledEvent

Summary of Event APIs ^TOP^