Print

Structure of a Palm Application, Part 1
Pages: 1, 2, 3

Example 5-2 shows the code for a typical AppEventLoop.



Example 5-2: Typical AppEventLoop

void AppEventLoop(void)
{
  Err error;
  EventType event;
 
  do {
    EvtGetEvent(&event, evtWaitForever);
    
    if (! SysHandleEvent(&event))
      if (! MenuHandleEvent(0, &event, &error))
        if (! AppHandleEvent(&event))
          FrmDispatchEvent(&event);
 
  } while (event.eType != appStopEvent);
}
Handling events with EvtGetEvent

This Event Manager routine's sole job is to get the next event from the queue. It takes as a second parameter a timeout value (in ticks--SysTicksPerSecond returns the units, which is 100 on all devices as of mid-2001). EvtGetEvent returns when either an event has occurred, or when the timeout value has elapsed (in which case it fills in an event code of nilEvent).

TIP:   EvtGetEvent can actually return with a nilEvent at any time, regardless of whether the timeout value has elapsed.

We don't have anything to do until an event occurs (this application has no idle-time processing to do), so we pass the evtWaitForever constant, specifying that we don't want a timeout.

The Event Queue and Application Event Loop

At this point, it is worth looking in more detail at the events that are received from EvtGetEvent. Events can be of all different types: anything from low-level to high-level ones. In fact, one useful way to look at a Palm application is simply as an event handler--it hands all sorts of events off to various managers, which in turn may post new events back to the queue where they will get handled by other event handlers. We will discuss more sophisticated examples of this later (see "Scenarios" later in this chapter), but for now we will look at a very simple set of events to get an idea of how all this works together. Imagine the user has our application open and taps the stylus on the screen in the area of the silk-screened menu button. The first time through the event queue the SysHandleEvent routine handles the event, interprets it, and creates a new event that gets put back in the queue (see Figure 5-1).

When this new event comes through the loop, it will get passed through SysHandleEvent and on to the MenuHandleEvent as it is now recognizable as a menu request (see Figure 5-2). MenuHandleEvent will display the menu bar and drop down one of the menus. If the user now taps outside of the menu, the menus will disappear.

Figure 5-1. An event in the event loop

Figure 5-2. A regurgitated event in the event loop

If a menu item is selected, however, then a new event is generated and posted to the queue. This event is retrieved by the event loop where it is passed through SysHandleEvent, and then on to MenuHandleEvent, and continues until some code handles the menu item. Given the way this process works, you can see that the different managers are interested in different types of events. Keeping this in mind, let's now return to our code and look at the event loop and the four routines in it.

The Four Routines of the Main Event Loop

You have briefly seen the four routines and their order in the main event loop. Here is a more detailed look at the responsibilities of each routine.

SysHandleEvent

if (! SysHandleEvent(&event))

The first routine in the loop is always SysHandleEvent as it provides functionality common to all Palm applications. For example, it handles key events for the built-in application buttons. It does so by posting an appStopEvent to tell the current application to quit. After the application quits, the system launches the desired application.

It also handles pen events in the silk-screened area (the graffiti input area and the silk-screened buttons). For example, if the user taps on Find, SysHandleEvent will completely handle the Find, returning only when the Find is done.

SysHandleEvent likewise handles the Power key, dealing with putting the device to sleep and/or turning backlighting on or off, depending on the duration of the Power key press.

MenuHandleEvent

if (! MenuHandleEvent(0, &event, &error))

The second routine in our event loop is MenuHandleEvent. As you might have imagined, the MenuHandleEvent handles events involving menus. These events occur when a user taps on the following:

  • Menu silk-screen button. The function finds the menu bar for the current form and displays it by creating a window.
  • Title of a form. This acts the same way as if the user tapped on the Menu silk-screen button.
  • Somewhere else while a menu is being displayed. The function closes the menu when the user taps outside of it.

As would be expected, it closes the menu and menu bar if the user taps on a menu item. At this point, it posts a menu event that will be retrieved in a later call to EvtGetEvent.

The first parameter to MenuHandleEvent specifies which menu to handle (0 means the current menu and is what you'll always pass). The last parameter is a pointer to an error return result; note, however, that virtually all applications (including ours) ignore the error result.

Unlike all other managers, the Menu Manager routines use a four-character prefix (Menu) rather than the three characters common to the others. In addition, unlike SysHandleEvent and FrmDispatchEvent, which take only the event and return a Boolean, MenuHandleEvent takes two extra parameters. (We get the feeling that whoever wrote the Menu Manager skipped some of the meetings on API design that the rest of the OS team attended.)

AppHandleEvent

if (! AppHandleEvent(&event))

The third routine, AppHandleEvent, is also a standard part of the event loop and is responsible for loading forms and associating an event handler with the form. This routine handles any events that it wants to process application-wide (as opposed to the majority of events, which are form-specific). This code is not part of the OS; you write it. However, it will be very similar from application to application. Example 5-3 shows a typical example of the routine.

Example 5-3: Typical AppHandleEvent (app-specific portions are emphasized)

static Boolean AppHandleEvent(EventPtr event)
{
  UInt16 formId;
  FormPtr form;

  if (event->eType == frmLoadEvent) {
    // Load the form resource.
    formId = event->data.frmLoad.formID;
    form = FrmInitForm(formId);
    ErrFatalDisplayIf(!form, "Can't initialize form");
    FrmSetActiveForm(form);

    // Set the event handler for the form.  The handler of the currently
    // active form is called by FrmHandleEvent each time it receives an event.
    switch (formId) {
    case MainForm:
      FrmSetEventHandler(form, MainFormHandleEvent);
      break;

    case SecondForm:
      FrmSetEventHandler(form, SecondFormHandleEvent);
      break;

    default:
      ErrFatalDisplay("Invalid Form Load Event");
      break;

    }
    return true;
  } else 
    return false;
}

A frmLoadEvent is a request to load a particular form. It is our responsibility to load the requested form (the OS won't load a form automatically). We initialize the specified form ID (FrmInitForm) and then make it the one-and-only active form (FrmSetActiveForm). Finally, we set the form's event handler (FrmSetEventHandler). If you create a new form, you'll write an event handler for it, and then add a new case to the switch statement, setting the event handler.

FrmDispatchEvent

FrmDispatchEvent(&event);

This fourth and last routine in the event loop is the one that indirectly provides form-specific handling. FrmDispatchEvent calls FrmHandleEvent, which provides standard form functionality (for example, a pen-down event on a button highlights the button, a pen-up on a button posts a ctlSelectEvent event to the event queue). Cut, copy, and paste in text fields are other examples of functionality handled by FrmHandleEvent.

In order to provide form-specific handling, FrmDispatchEvent calls the form's installed event handler first before calling FrmHandleEvent. If the event handler returns true, no further processing happens. If it returns false, FrmDispatchEvent will then call FrmHandleEvent, which will provide the standard functionality. Thus, your event handler always gets the first crack at events occurring within a form. Here's pseudocode for FrmDispatchEvent to give you an idea of what it will look like:

Boolean FrmDispatchEvent(EventType *event)
{
  Boolean handled = result of calling Form's event handler;
  if (handled)
    return true;
  else
    return FrmHandleEvent(event);
}
In the next installment create a simple Palm application

Neil Rhodes and Julie McKeehan are experienced authors who, through their company, Calliope Enterprises, work closely with Palm Computing to develop new training materials, materials that are based on this book.

Palm OS Programming

Related Reading

Palm OS Programming
The Developer's Guide
By Neil Rhodes, Julie McKeehan

Return to the Wireless DevCenter