RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Tweak Brew's Component Interfaces to Tackle Any Task : Page 4

Brew's impressive collection of component interfaces usually makes your life easier, but there just isn't an interface for everything. What happens when an interface is almost perfect for your task...but not quite? Find out how to extend an interface to use its existing functionality while adding something completely different.

Handling User Input
User input within the IOManager is a little trickier. Because you don't want to re-write the various input methods (multitap and potentially T9 or iTap) from scratch, it makes sense to leverage the ITextCtl to do front-line processing of keystrokes on the numeric keypad. This is not without its limitations, however; it essentially makes input line-centric rather than character-centric, but this is a reasonable compromise for these test applications. Consequently, this is the flow of input:
  • When the IOManager is active, the client application passes all events to the IOManager via the IOMANAGER_HandleEvent method, just as if it were a control.
  • Events are shared with the HTML viewer, permitting up/down scrolling of the HTML viewer, and the text control, letting the text control manage all user input.
  • When you press select, the contents of the text control are appended to the end of the input buffer and written to the HTML viewer and the text control emptied.
As the description indicates, this flow of control begins with IOManager_HandleEvent, the implementation of IOMANAGER_HandleEvent:

boolean IOManager_HandleEvent(IOManagerCls *pMgr, 
                               AEEEvent eCode, uint16 wParam, 
                               uint32 dwParam)
  SIOManager *pThis = (SIOManager*)pMgr;
  boolean bResult;

  bResult = IHTMLVIEWER_HandleEvent(pThis->m_pIHtml, eCode,
                                    wParam, dwParam);
  if ( eCode != EVT_KEY ||
       ( eCode == EVT_KEY && 
         wParam != AVK_UP && wParam != AVK_DOWN ) )
    bResult = ITEXTCTL_HandleEvent(pThis->m_pITextCtl, eCode, 
                                    wParam, dwParam);
  if ( eCode == EVT_KEY && wParam == AVK_SELECT )
    _HandleRead( pThis );
  return bResult;
The _HandleRead function does the necessary buffering of the input into the m_szInput buffer (Listing 4).

The interesting part of the code is at the end, after converting the contents of the text control to a C string, writing the contents to the output buffer, and copying them to the input buffer. That's where the interface to the client application happens, when the IOManager uses ISHELL_Resume to kick off the client's Readable callback to indicate that there's more data available. In turn, the client can call IOMANAGER_Read, which resolves to IOManager_Read:

int32 IOManager_Read(IOManagerCls * pMgr, void * pBuffer, uint32 dwCount)
  SIOManager *pThis = (SIOManager*)pMgr;
  int nAmount = MIN( dwCount, STRLEN( pThis->m_szInput ) );

  if ( !pBuffer || !dwCount ) return 0;
  STRNCPY(pBuffer, pThis->m_szInput, nAmount);
  // And move the buffer's contents over by that amount.
  MEMMOVE(pThis->m_szInput, pThis->m_szInput + nAmount, 
          BUFFSIZE - nAmount);
  return nAmount;
Naturally, for any of this to happen, the client application must first register a callback using IOMANAGER_Readable, just as if it was using a file or the higher-level abstract IAStream interface: void IOManager_Readable(IOManagerCls * pMgr, PFNNOTIFY pfn, void * pUser) { SIOManager *pThis = (SIOManager*)pMgr; if ( !pfn && !pUser ) { IOManager_Cancel( pMgr, pfn, pUser ); } else { pThis->m_bInvokeReadable = TRUE; CALLBACK_Init( &pThis->m_cbReadable, pfn, pUser ); if ( STRLEN( pThis->m_szInput ) ) { ISHELL_Resume( pThis->m_pIShell, &pThis->m_cbReadable ); pThis->m_bInvokeReadable = FALSE; } } } Readable first stores the newly-provided callback information (canceling the existing callback if the provided callback is NULL) and then actually invoking the callback if there's any data to read. Otherwise, the code you saw in _HandleRead will invoke the callback once more data has been read. The m_bInvokeReadable flag ensures that you call a given readable callback only once; per the Brew documentation, Readable must be called each time you want to know when there's more data available to read.

Between the implementation of Readable and Read, this allows your interface's clients to treat the interface just as if it were any other IAStream implementation.

Component Riffing
While Brew doesn't provide a framework for customizing the behavior of individual components, it does let you easily implement new components to existing interfaces using the INHERIT_ macros provided. Using an existing interface and providing your own functionality is a good approach when you recognize the fit between your component and an existing component's interface, and you can capitalize on re-using the documentation and knowledge about a specific interface, helping speed your overall development.

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. Ray 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.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date