Tweak Brew’s Component Interfaces to Tackle Any Task

Tweak Brew’s Component Interfaces to Tackle Any Task

ualcomm Brew’s component-oriented architecture and rich collection of components?interfaces, in Brew parlance?makes it easy to find a component to accomplish a particular task, such as decompressing an incoming stream (try IUnzipAStream) or rendering text (an IFont instance). Sometimes, however, you know the job that needs to be done, but there’s just no component that fits?even though there’s a likely-looking interface that would do the job. This begs the question: why re-invent the wheel if you don’t have to? It’s bad enough writing the code, but if you can get away with using an existing Brew interface to encapsulate your functionality, at least you don’t have to write much of the corresponding documentation.

This article will show you how to do just that: start with an existing interface (the IFile interface, in this case) and write an extension that implements that interface while doing something totally different: emulating a simple standard input/output control to facilitate debugging when porting code to Brew.

Introducing the IOManager

Figure 1. IOManager: Here’s the IOManager in action.

The IOManager is a subclass of the IFile interface that permits users to interact with an application using a scrolling text window and input line?much like a simple dumb terminal. Utterly useless for the average commercial application, IOManager is handy if you’re putting together a little test application to prove something, such as when you need to unit test code you’ve ported to Brew or isolate a piece of code to determine the cause of a bug. Figure 1 shows the IOManager in action.

Click here to download the source code accompanying this article.

While the IOManager could have been written as either a completely custom interface or a subclass of the IControl interface, in this article, it’s a subclass of the IFile interface for a couple of reasons. First, its functionality closely resembles that of the old C-style stdin and stdout ports, accessible via file operations in traditional C. This correspondence seemed to carry over nicely in the implementation, and with a little work, ported code can access the IOManager‘s capabilities the same way as other files during testing. Second, for demonstration purposes, the IFile interface is a little more complex (for example, the Readable/Read paradigm requiring asynchronous callbacks), making the IFile interface a better example when showing you how to go about implementing to a Brew interface.

Internally, the IOManager consists of two separate controls, an ITextCtl to manage user input, and an IHTMLViewer, used to show information written to the IOManager or entered in the ITextCtl. Information passed to the IOManager via its Write method inherited from IFile is displayed in the IHTMLViewer, and anything entered in the text control and confirmed using the SELECT key is available to the client application through the IOManager‘s Read method. Structurally, the data for the IOManager implementation looks like this:

struct SIOManager{  //VTBL pointer always comes first.  IOManagerClsVtbl *pvt;  IShell *m_pIShell;  IModule *m_pIModule;  uint16  m_nRefs;  AEECallback  m_cbReadable;  boolean m_bInvokeReadable;  AEECallback m_cbWritable;  AEERect  m_rc;  AEERect  m_rcHtml;  char m_szInput[ BUFFSIZE ], m_szOutput[ BUFFSIZE ],        m_szHTML[ BRSIZE * BUFFSIZE ];  ITextCtl *m_pITextCtl;  IHtmlViewer *m_pIHtml;  IDisplay *m_pIDisplay;};

Most of these fields will be explained in subsequent sections of this article. For now, you should know that:

  • The implementation’s virtual table must be first, because Brew uses C casts to convert between an extension’s interface (its virtual table) and the private data used by the interface (more on this in the next section).
  • The extension uses three buffers: a buffer to contain what the program has written to the extension to display, a buffer to contain what you’ve entered via the text control, and a buffer containing the combined contents of the two formatted as HTML for display to the user using the HTML viewer control (these are the character strings m_szInput, m_szOutput, and m_szHTML, respectively).
  • The m_pITextCtl text control provides all user input handling, and the m_pIHtml HTML viewer control handles displaying both programmatically written and user-entered data (the contents of m_szHTML).

The Interface
Overloading the IFile manager is similar to building out any other Brew extension with one major catch: the virtual table, or, vtable. You must meet, in exactly the same order, the API contract of the IFile object as set out in the vtable. Here’s how ours looks:

struct IOManagerClsVtbl{	INHERIT_IFile(IOManagerCls);	boolean (*fnHandleEvent)(IOManagerCls *pMgr, AEEEvent eCode,                              uint16 wParam, uint32 dwParam);	uint32  (*fnDraw)(IOManagerCls *pMgr);	uint32	(*fnSetRect)(IOManagerCls *pMgr, AEERect *pRect);};

Again, if the beginning of your vtable for your object doesn’t exactly mimic its Brew counterpart you’ll experience some very interesting crashes. Brew helps you out in this endeavor by defining the “INHERIT_IFile” macro. This will declare all the IFile functions in the correct order for you. Not all Brew extensions have an associated inherit macro, but you should take advantage of them whenever you can. Any functions you wish to add other than what’s in the Brew object must be added afterwards. If you get your vtable right, you can now use the new overloaded object anywhere that an IFile object could be used. However, if there are additional functions outside the IFile API, you need to write new macros with which to access them.

// IAStream Methods#define IOMANAGER_AddRef(p) GET_PVTBL(p,IOManager)->AddRef(p)#define IOMANAGER_Release(p) GET_PVTBL(p,IOManager)->Release(p)#define IOMANAGER_Readable(p, pfn,pu) GET_PVTBL(p,IOManager)->Readable(p,pfn,pu)#define IOMANAGER_Read(p, b, c)  GET_PVTBL(p,IOManager)->Read(p, b, c)#define IOMANAGER_Cancel(p,pfn,pu) GET_PVTBL(p,IOManager)->Cancel(p,pfn,pu)// IFile Methods#define IOMANAGER_Write(p, b, c) GET_PVTBL(p,IOManager)->Write(p, b, c)#define IOMANAGER_GetInfo(p, pi) GET_PVTBL(p,IOManager)->GetInfo(p, pi)#define IOMANAGER_Seek(p, o, po) GET_PVTBL(p,IOManager)->Seek(p, o, po)#define IOMANAGER_Truncate(p, t) GET_PVTBL(p,IOManager)->Truncate(p, t)#define IOMANAGER_GetInfoEx(p,pi) GET_PVTBL(p,IOManager)->GetInfoEx(p,pi)#define IOMANAGER_SetCacheSize(p,n) GET_PVTBL(p,IOManager)->SetCacheSize(p,n)#define IOMANAGER_Map(p,ps,dws,pr,fl,dw) GET_PVTBL(p,IOManager)->Map(p,ps,dws,pr,fl,dw)// Control-like methods#define IOMANAGER_HandleEvent(p,e,w,dw) GET_PVTBL(p,IOManager)->HandleEvent(p,e,w,dw)#define IOMANAGER_Redraw(p) GET_PVTBL(p,IOManager)->Redraw(p)#define IOMANAGER_SetActive(p,b) GET_PVTBL(p,IOManager)->SetActive(p,b)#define IOMANAGER_SetRect(p,prc) GET_PVTBL(p,IOManager)->SetRect(p,prc)

Above, you can see the extra macros written to extend the IOManager‘s function set. With these in place, you can now talk to your new object using both your IOManager macros and Brew’s IFile calls. All Brew objects must have two structures: the external vtable and the internal object listed in the previous section. At the beginning of each function, you have to cast the external vtable object to your internal object?as shownbelow:

static uint32 IOManager_AddRef(IOManagerCls *pMgr){	SIOManager *pThis = (SIOManager*)pMgr;	pThis->m_nRefs++;	return pThis->m_nRefs;}

It is through the cast on the first line of the function that you turn a simple list of functions into a structure containing your private data. The vtable is declared in the public header file while the internal structure should be defined in the .c file. Remember to declare all your function implementations ‘static‘ (as has been done above) so they cannot be referenced directly outside your extension. In this way, the vtable becomes the interface to your object. All calls into your static functions must be accessed through the macros that in turn lookup the function address in your vtable.

Construction and Destruction of the Extension
If you’ve ever written a Brew extension before, this source should look very familiar. For those of you who haven’t done it before, here’s a quick tour of the process we went through to design and implement the IOManager. The first step is to obtain a new class ID from Qualcomm’s developer Web site. In the process, you should receive a .bid file. Then add a new extension to the .mif file for your application. When you call ISHELL_CreateInstance with the new class ID, your AEEClsCreateInstance function will be called. Next, add code to that function to check for your new class ID and call your IOManagerCls_New.

Now, take a look at the IOManagerCls_New function, shown in Listing 1.

Your first step is to allocate enough resources for both the vtable and your internal data. With memory in hand, you must link every entry in the vtable with its corresponding internal function. After you’ve got all your function pointers directed in the right places, you have to link your internal object (pThis) with the vtable. This can be done by hand, but it’s much easier to use the “INIT_VTBL” macro provided by Brew. Once that’s established, you need to hold on to references of your shell and module objects and set the module double pointer to your object pointer w. The ISHELL_CreateInstance will pass your object back to the caller and finally initialize your own internal variables.

If all this seems confusing, don’t worry, it is. This is just a brief foray into extension creation. There are other articles that get much more in depth about creating Brew objects. That said, perhaps the easiest way to set up a new object is to copy and paste one from an example and modify the function calls to suit your needs. For an in-depth look at creating new extensions in C and C++, stay tuned to DevX for a future article on the topic.

Tearing down the object is a simple matter. You just release any internal objects and data, free your vtable, and then release the memory you allocated for your extension The code below shows how it’s been done for the IOManager:

uint32 IOManager_Release(IOManagerCls *pMgr){	SIOManager *pThis = (SIOManager*)pMgr;		pThis->m_nRefs--;	if(pThis->m_nRefs != 0) return pThis->m_nRefs;	CALLBACK_Cancel( &pThis->m_cbReadable );	CALLBACK_Cancel( &pThis->m_cbWritable );	FREE_VTBL(pThis->pvt, IOManagerCls);	IHTMLVIEWER_Release( pThis->m_pIHtml );	ITEXTCTL_Release( pThis->m_pITextCtl );	IDISPLAY_Release( pThis->m_pIDisplay );	if ( pThis->m_pIModule ) IMODULE_Release( pThis->m_pIModule );	ISHELL_Release( pThis->m_pIShell );	FREE(pThis);	return 0;}

The code in the release function really speaks for itself. Check to make sure you’re releasing the last reference, cancel any timers you might have set, free your vTable, release your objects, and you’re done.

Handling Incoming Data
Because the IOManager implements IFile, writing text to the display is as simple as calling IOMANAGER_Write:

  nWritten = IOMANAGER_Write(pThis->m_pIIO, sz, STRLEN( sz ));

Under the hood, the method is implemented in IOManager_Write, which must:

  • Determine and create sufficient space in the m_szOutput string for the newly written data, removing old data if needed.
  • Update the HTML stored in m_szHTML with the new data, converting line feeds to the HTML line break directive
  • Redraw the screen with the new content by reloading the HTML viewer with the freshly recreated HTML.

Listing 2 shows IOManager_Write.

The bulk of Listing 2’s code is the skullduggery necessary to handle trimming old content in the event that there’s more data to put in the buffer than the buffer can hold?when the if statement at the beginning of the function holds. If it holds, you search the buffer for the first line feed after the requisite number of characters to be written to the buffer, and use MEMMOVE to slide everything remaining in the buffer to the beginning of the buffer to make room for the newly-written content. The content itself is written to the buffer using STRCAT.

Rather than immediately reprocessing the HTML buffer and redrawing the display, defer these activities until the next pass through the event handler using Brew’s asynchronous resume facility, so that in the event of many small writes to the IOManager you’ll perform this costly operation only once. This is analogous to buffering file system writes and flushing only infrequently, because file system writes are more costly than buffer writes. Do this by scheduling a resumed invocation of the _HandleWrite method, which does the heavy lifting (Listing 3).

This is just more string-munging. Walk the m_szOutput buffer looking for instances of the linefeed character ‘
.’ When you encounter one, write out the corresponding HTML line break
tag to the HTML buffer m_szHTML. For all other characters, simply copy the character straight across. Once this is done, erase the existing contents of the control on the display, and set the control’s data to contain the newly-created HTML.

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.



Share the Post:
Poland Energy Future

Westinghouse Builds Polish Power Plant

Westinghouse Electric Company and Bechtel have come together to establish a formal partnership in order to design and construct Poland’s inaugural nuclear power plant at

EV Labor Market

EV Industry Hurting For Skilled Labor

The United Auto Workers strike has highlighted the anticipated change towards a future dominated by electric vehicles (EVs), a shift which numerous people think will

Soaring EV Quotas

Soaring EV Quotas Spark Battle Against Time

Automakers are still expected to meet stringent electric vehicle (EV) sales quotas, despite the delayed ban on new petrol and diesel cars. Starting January 2023,

Affordable Electric Revolution

Tesla Rivals Make Bold Moves

Tesla, a name synonymous with EVs, has consistently been at the forefront of the automotive industry’s electric revolution. The products that Elon Musk has developed

Poland Energy Future

Westinghouse Builds Polish Power Plant

Westinghouse Electric Company and Bechtel have come together to establish a formal partnership in order to design and construct Poland’s inaugural nuclear power plant at the Lubiatowo-Kopalino site in Pomerania.

EV Labor Market

EV Industry Hurting For Skilled Labor

The United Auto Workers strike has highlighted the anticipated change towards a future dominated by electric vehicles (EVs), a shift which numerous people think will result in job losses. However,

Soaring EV Quotas

Soaring EV Quotas Spark Battle Against Time

Automakers are still expected to meet stringent electric vehicle (EV) sales quotas, despite the delayed ban on new petrol and diesel cars. Starting January 2023, more than one-fifth of automobiles

Affordable Electric Revolution

Tesla Rivals Make Bold Moves

Tesla, a name synonymous with EVs, has consistently been at the forefront of the automotive industry’s electric revolution. The products that Elon Musk has developed are at the forefront because

Sunsets' Technique

Inside the Climate Battle: Make Sunsets’ Technique

On February 12, 2023, Luke Iseman and Andrew Song from the solar geoengineering firm Make Sunsets showcased their technique for injecting sulfur dioxide (SO₂) into the stratosphere as a means

AI Adherence Prediction

AI Algorithm Predicts Treatment Adherence

Swoop, a prominent consumer health data company, has unveiled a cutting-edge algorithm capable of predicting adherence to treatment in people with Multiple Sclerosis (MS) and other health conditions. Utilizing artificial

Personalized UX

Here’s Why You Need to Use JavaScript and Cookies

In today’s increasingly digital world, websites often rely on JavaScript and cookies to provide users with a more seamless and personalized browsing experience. These key components allow websites to display

Geoengineering Methods

Scientists Dimming the Sun: It’s a Good Thing

Scientists at the University of Bern have been exploring geoengineering methods that could potentially slow down the melting of the West Antarctic ice sheet by reducing sunlight exposure. Among these

why startups succeed

The Top Reasons Why Startups Succeed

Everyone hears the stories. Apple was started in a garage. Musk slept in a rented office space while he was creating PayPal with his brother. Facebook was coded by a

Bold Evolution

Intel’s Bold Comeback

Intel, a leading figure in the semiconductor industry, has underperformed in the stock market over the past five years, with shares dropping by 4% as opposed to the 176% return

Semiconductor market

Semiconductor Slump: Rebound on the Horizon

In recent years, the semiconductor sector has faced a slump due to decreasing PC and smartphone sales, especially in 2022 and 2023. Nonetheless, as 2024 approaches, the industry seems to

Elevated Content Deals

Elevate Your Content Creation with Amazing Deals

The latest Tech Deals cater to creators of different levels and budgets, featuring a variety of computer accessories and tools designed specifically for content creation. Enhance your technological setup with

Learn Web Security

An Easy Way to Learn Web Security

The Web Security Academy has recently introduced new educational courses designed to offer a comprehensible and straightforward journey through the intricate realm of web security. These carefully designed learning courses

Military Drones Revolution

Military Drones: New Mobile Command Centers

The Air Force Special Operations Command (AFSOC) is currently working on a pioneering project that aims to transform MQ-9 Reaper drones into mobile command centers to better manage smaller unmanned

Tech Partnership

US and Vietnam: The Next Tech Leaders?

The US and Vietnam have entered into a series of multi-billion-dollar business deals, marking a significant leap forward in their cooperation in vital sectors like artificial intelligence (AI), semiconductors, and

Huge Savings

Score Massive Savings on Portable Gaming

This week in tech bargains, a well-known firm has considerably reduced the price of its portable gaming device, cutting costs by as much as 20 percent, which matches the lowest

Cloudfare Protection

Unbreakable: Cloudflare One Data Protection Suite

Recently, Cloudflare introduced its One Data Protection Suite, an extensive collection of sophisticated security tools designed to protect data in various environments, including web, private, and SaaS applications. The suite

Drone Revolution

Cool Drone Tech Unveiled at London Event

At the DSEI defense event in London, Israeli defense firms exhibited cutting-edge drone technology featuring vertical-takeoff-and-landing (VTOL) abilities while launching two innovative systems that have already been acquired by clients.

2D Semiconductor Revolution

Disrupting Electronics with 2D Semiconductors

The rapid development in electronic devices has created an increasing demand for advanced semiconductors. While silicon has traditionally been the go-to material for such applications, it suffers from certain limitations.

Cisco Growth

Cisco Cuts Jobs To Optimize Growth

Tech giant Cisco Systems Inc. recently unveiled plans to reduce its workforce in two Californian cities, with the goal of optimizing the company’s cost structure. The company has decided to

FAA Authorization

FAA Approves Drone Deliveries

In a significant development for the US drone industry, drone delivery company Zipline has gained Federal Aviation Administration (FAA) authorization, permitting them to operate drones beyond the visual line of

Mortgage Rate Challenges

Prop-Tech Firms Face Mortgage Rate Challenges

The surge in mortgage rates and a subsequent decrease in home buying have presented challenges for prop-tech firms like Divvy Homes, a rent-to-own start-up company. With a previous valuation of

Lighthouse Updates

Microsoft 365 Lighthouse: Powerful Updates

Microsoft has introduced a new update to Microsoft 365 Lighthouse, which includes support for alerts and notifications. This update is designed to give Managed Service Providers (MSPs) increased control and

Website Lock

Mysterious Website Blockage Sparks Concern

Recently, visitors of a well-known resource website encountered a message blocking their access, resulting in disappointment and frustration among its users. While the reason for this limitation remains uncertain, specialists