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 2

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.

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
	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;
	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;

	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 );


	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.

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