Back to article

Extending Event Handling with the BREW UI Toolkit

February 22, 2007

One of the challenges of writing a large BREW application is keeping your event handling code organized. Although breaking up your event handler by application state and/or event type can help, the result becomes a proliferation of conditionals and function dispatches in your application's main event handler. With the advent of the BREW uiOne Toolkit (BUIT), however, there's a better approach: the HandlerDesc and the IHandler interface.

Enter the HandlerDesc

The purpose of the HandlerDesc is simple: It lets you chain functions by storing function pointers and associated context data at run time. By creating a list of event handlers, and having each event handler chain to the next, as long as the first event handler invokes the first event handler in the chain and checks its return value before continuing to process the event, user-level functions can hook the behavior of a component's provided event handler. HandlerDescs work with any BREW interface that implement the IHandler interface. This includes the BUIT widgets and forms, making it an important part of building BUIT-based applications.

Using a HandlerDesc to do this is easy:

  1. Write your custom event handling code in a function that conforms to the event-handling interface, taking the component, event code, and event arguments.
  2. After creating an instance of the component whose event handler you want to hook, create a HandlerDesc structure (it must be on the heap somewhere, not on the stack) and initialize it with your custom event handler.
  3. Finally, link your event handler to the instance of your component by using its SetHandler method.

You also can use a HandlerDesc to hook a component's destruction. This is important if you want to destroy component-related context data at the same time that the component is freed.

Under the Hood

Under the hood, a HandlerDesc structure is just the information that encapsulates your event handler:

typedef struct {
   PFNHANDLER        pfn;
   void *            pCxt;
} HandlerDesc;

Of course, as with most BREW structures, you shouldn't access these slots directly. Instead, BREW provides a macro, HANDLERDESC_Init, to do so:

HANDLERDESC_Init( &pMe->handler, MyEventHandler, pMe,
                  MyFreeFunction );

The context data (in this case, pMe), is passed to both the event handler (MyEventHandler) and the destruction-time handler (MyFreeFunction) when the component invokes either of these functions.

You still need to register the HandlerDesc with the event-handling component for it to do anything. This is done by using the component's SetHandler call:

IHANDLER_SetHandler( (IHandler*)pMe->p, &pMe->handler );

This causes the Handler to register your HandlerDesc within its context, ensuring that it gets called at an appropriate time during the event handling process instead of its native event handler.

Once your event handler handles incoming events, it must chain to the existing event handler. That's because the IHANDLER_SetHandler interface replaces the existing event handler with your event handler; if you still want the default event-handling behavior of the original component, you must invoke its event handler as well:

result = HANDLERDESC_Call( &pMe->selectHandler, evt, wParam,
                           dwParam );

Precisely when you do this depends on the behavior you seek. In most cases, you want to patch an existing event handler, either handling unhandled events or changing how an event is handled. In that case, your event handler should handle incoming events first, performing whatever custom actions you desire, and then invoke HANDLERDESC_Call, returning its result so that previous event handlers know whether or not the event was handled.

Examining a HandlerDesc in Action

A common reason to trap an event is to know when a particular form has become active—say, to play a sound when a screen is drawn or ensure that the screen has the latest contents available from a persistent store. Because IForm implements IHandler (as does IWidget), a HandlerDesc is the idea way to solve this problem. (Note that in the following example I've omitted error checking from common routines such as CreateInstance for brevity.)

First, I need a custom event handler. This might be as simple as this one:

static boolean MyHandleEvent( SMyForm *pMe, AEEEvent evt,
                              uint16 wParam, uint32 dwParam )
   if ( evt == FID_ACTIVE )
      // Use the data in pMe to play a sound, update the model for
      // the widgets in this form, or whatever.
   return HANDLERDESC_Call( pMe->selectHandler, evt, param,
                            wParam, dwParam );

I also need a cleanup function, say, to clean up the IMedia player used to play my custom sound, or to close the persistent data store:

static void MyCleanup( SMyForm *pMe )
   // Free up anything in pMe that is on the heap.
   IFORM_Release( pMe->pIForm );
   // Then release my context
   FREE( pMe );

Next, I create the data container that will hold the form and HanderDesc, as well as the form. In the process, I'll register my event handler and cleanup function with the form as well:

IForm * MyForm_Create( CApp *pMe )
   SMyForm *pForm = MALLOCREC( SMyForm );

   // Do any other necessary setup here.

   ISHELL_CreateInstance( pMe->a.m_pIShell, AEECLSID_FORM,
                          (void **)&pForm->pIForm );
   HANDLERDESC_Init( &pMe->handler, MyHandleEvent, pMe,
                     MyCleanup );
   IHANDLER_SetHandler( (IHandler*)pMe->pIForm, &pMe->handler );

   return pForm->pIForm;

I now can push the new form on the form stack; as a consequence, MyHandleEvent will be invoked with a FID_ACTIVE event, and I can process the event however I please. Once it's processed, I can pass the event on to the form's event handler, and it operates as it should. That's all there is to it!


The HandlerDesc mechanism is a powerful way to adapt and extend a fixed component's event handler. Not only is it a crucial part of building an application using the BUIT, but you also can use the same interface in your applications with custom components for greater extensibility, too!

Related Resource

Qualcomm BREW (incluing the uiOne Toolkit):

About the Author

Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today's wireless devices. He is the author of several books on software development, including eBay Application Development and Software Development for the QUALCOMM BREW Platform, both available from Apress, and is an active Amateur Radio operator. Contact Ray at

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date