Thursday, August 23, 2007

Improving Code Completion in V9

The CodeWarior tools are developed by several different groups. While I'm responsible for the Palm OS tools, I rely on technology from other teams, most notably the IDE team. Earlier in 2002, they shipped IDE 4.3, our first IDE with true code completion. This was implemented only for Java at the time, but they worked hard over the next few months to get the same thing working for C and C++. They mostly succeeded, but their implementation had some problems that caused it to fail for many cases. In their continuing work on IDE 5, they've been improving it, but there are still some quirks of which you need to be aware in order to use this feature well.

How Does Code Completion Work?
Code completion has two parts: symbol acquisition and context discovery. Symbol acquisition is the process by which entries are put into the IDE's symbol browser database. You can see most of this database using the View/Browser Contents command.


The "Build Extras" panel of your project controls how this database is built. There are two options: compiler or code parser. The compiler method is the same one used in previous versions of CodeWarrior. With this, the compiler reports back to the IDE about the functions it compiled and the the macros and typedefs it saw. This data isn't very detailed, since it doesn't include prototypes for funtions that weren't also compiled, and it also doesn't include argument information for functions or macros. This data also requires that your code compile to be created. The IDE supports merging data from several projects -- if you link your project to another project and setup a dependency relationship, then the compiled browser data from that project will be imported into your own. This is how we setup browse info for precompiled headers in the past. See my article
Palm OS Precompiled Headers for details.

The other option on the Build Extras panel is "Code Parser". This is usually the preferred option. When you pick this, the IDE uses its own C/C++ parser to read through all your source files, picking up information straight from your source and headers. This usually works, but there are some tweaks that have to be made for this step to succeed.

First, you need sane values for the Prefix File and Macro File settings. The prefix file setting should match your prefix setting in the C/C++ Compiler panel, but with one exception. If you're directly using a precompiled header as a prefix, you need to mention the file that generates that PCH here instead. Our default for Palm OS projects is "PalmOS.win.pch", the header that builds the PalmOS_Header precompiled header file.
The Macro File setting is very important for completion using the Palm OS headers. Normally, the code parser doesn't try to expand macros it sees in the source or header files. However, if you have macros that affect how the code is parsed, you need to specify them in a separate source file and mention it here. The Palm OS headers are full of these kind of macros, since all of the Palm OS API functions use them to define how they are called. We have provided a good default file, PalmOSMacros.h, which lives in the "CW for Palm OS Support" folder. You need a "\{Compiler\}CW for Palm OS Support" system access path in your project to see this file. Without this, the parser will get very confused with the Palm OS headers and just give up on them, which means you won't get completions for the Palm OS API calls.


OK, so if all this is working, then shortly after opening your project, you should see all the symbols turn pretty colors and a big database of items in the View/Browser Contents dialog. Now, we come to context discovery.
Even when you're using the compiler to gather symbols, you're also using the code parser to let the IDE know where you are in your source file. The IDE uses this information to provide itself with context about what symbols could be completed at the cursor location. If you look at the "Editor/Code Completion" settings in the IDE's preferences dialog, you'll see settings that control when it pops up a completion box. If the IDE isn't providing help on its own, you can manually bring up help using the alt-period key. This keystroke can be changed by altering the key binding of the "Edit/Complete Code" command. The completion window provides both symbol names, and also type information.


Why Doesn't This Work?
Here are two reasons:
1) The code parser has bugs that make it give up on some source files. One bug that made it into the IDE build that we shipped on V9 involved the binary and operator. The parser sometimes dies when it sees it, since its parsing it as a address-of operator and getting confused. This IDE also doesn't parse macros that have a space between the macro name and the argument list. Both of these bugs have been fixed for the 9.1 patch.
2) The PalmOSMacros.h file is incomplete. I built this file based on the Palm OS 4 SDK headers, and I missed some macros that are used in the Palm OS 5 SDK. A new version of this file is coming in the 9.1 patch.


Source :http://www.palmoswerks.com/stories/storyReader$124

Monday, August 20, 2007

How to programmatically open the find dialog

To programatically open the find dialog, you need to add a key down event to the eventqueue using the EvtAddEventToQueue function. Here is a code example for adding an eventto the queue:

/* Emulate a keydown event */
static void AddKeyDownEvent(WChar character)
{
EventType theEvent;
MemSet(&theEvent, sizeof(EventType), 0);

/* Create the key down event */
theEvent.eType = keyDownEvent;
theEvent.data.keyDown.chr = character;
theEvent.data.keyDown.keyCode = 0;
theEvent.data.keyDown.modifiers = commandKeyMask;

/* Add the event to the event queue */
EvtAddEventToQueue(&theEvent);
}
In this case, pass in a character code of vchrFind Some other possible values that youmay find useful for the character argument are:


vchrMenu - Shows the menu bar
VchrCommand - Simulate a command stroke
vchrLaunch - Opens the launcher
vchrCalc - Opens the calculator
VchrRonamatic - Simulates a stroke from the graffiti writing area to the top of the screenvchrGraffitiReference - Opens the graffiti reference
dialogvchrLock - Locks and powers off the unit
vchrBacklight - Toggles the state of the backlight

For a complete listing see Chars.h.

Overriding the silk screen buttons

In the main event loop, scan for the keyDown event before SysHandleEventgets to process it. If the character is vchrFind, you skip the rest of the event loop, effectively ignoring the find button.
For example:

static void EventLoop()
{ Err error; EventType event;
do

{
EvtGetEvent(&event, evtWaitForever);
// Eat all find key events

if (event.eType == keyDownEvent && event.data.keyDown.chr == vchrFind)
{
continue;
}
if (!SysHandleEvent(&event))

{
if (!MenuHandleEvent(0, &event, &error))
{
if (!ApplicationHandleEvent(&event))
{
FrmDispatchEvent(&event);
}
}
}
}while (event.eType != appStopEvent);
}

/***************************************OR*****************************************************/

PenBtnInfoPtr btnInfo;
UInt16 numButtons;

btnInfo = (const PenBtnInfoPtr)EvtGetPenBtnList(&numButtons); //Get the list of silk screen buttons.

for(int i =0; i
{
if((pEvent->data.keyDown.chr)==btnInfo[i].asciiCode)
{
FrmCustomAlert(GenericErrorAlert,"Silk Screen button selected","","");
return true;
}
}

Launching Application From SD Card

err=VFSImportDatabaseFromFile (VolRef, lapp, &card, &locid);

DmDatabaseInfo (card, locid, NULL, &attr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
attr=dmHdrAttrRecyclable;
DmSetDatabaseInfo (card, locid, NULL, &attr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

SysUIAppSwitch (card, locid, sysAppLaunchCmdNormalLaunch, cmdPBP);

This code copies database from memory card to main memory, sets it's attribute to 'delete it automatically after closing' and then runs it in main memory.
This works fine with one exception: if reset occurs during running this app, it remains in main memory. But I think that it happens even in case of standard launcher running app from card.

How to power off programmatically

Add the vchrPowerOff keyDownEvent to the queue
/***********************************************************************************/
static void Uti_PowerOff(void)
{
EventType event;
MemSet(&event,sizeof(event),0);
event.eType = keyDownEvent;
event.data.keyDown.chr = vchrPowerOff; event.data.keyDown.keyCode = 0;
event.data.keyDown.modifiers = commandKeyMask;
EvtAddEventToQueue(&event);
}

How to know if the palm is powering on or is going to sleep

Register for some notification and handle them as followsUnregister with SysNotifyUnregister() when no more neededYou can use a Feature (see How to store a permanent value... ) to store the program start time

/*********************************************************************************************/
static void RegisterForNotifications()
{
UInt16 cardNo=0;
UInt32 romVersion=0;
LocalID dbID=0;
FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
if(romVersion <>
return;
if(SysCurAppDatabase(&cardNo, &dbID)!=0)
return;
// Register the Notification you need
SysNotifyRegister(cardNo, dbID, sysNotifyEarlyWakeupEvent, NULL, sysNotifyNormalPriority, 0);
SysNotifyRegister(cardNo, dbID, sysNotifyLateWakeupEvent, NULL, sysNotifyNormalPriority, 0);
SysNotifyRegister(cardNo, dbID, sysNotifySleepRequestEvent, NULL, sysNotifyNormalPriority, 0);
}
/****************************************************************************************/

static void HandleNotifications(SysNotifyParamType *np)
{
if(np->notifyType==sysNotifyEarlyWakeupEvent)
{
// Do something
}
else if(np->notifyType==sysNotifyLateWakeupEvent)
{
// Do something
}
else if(np->notifyType==sysNotifySleepRequestEvent)
{
// Do something
}
}

/***************************************************************************************/

UInt32 PilotMain(UInt16 cmd,void *cmdPBP,UInt16 launchFlags)
{
switch(cmd)
{
case sysAppLaunchCmdSystemReset:
case sysAppLaunchCmdSyncNotify:
RegisterForNotifications();
break;
case sysAppLaunchCmdNotify:

case sysNotifySleepRequestEvent:
HandleNotifications((SysNotifyParamType *)cmdPBP);
break;
case sysAppLaunchCmdNormalLaunch:

... Normal code ...

}

Friday, August 17, 2007

How to exit from the application

/*Returns to launcher*/

static void Uti_Exit(void)
{
EventType event;
MemSet(&event,sizeof(event),0);
event.eType = keyDownEvent;
event.data.keyDown.chr = vchrLaunch;event.data.keyDown.modifiers = commandKeyMask;
EvtAddEventToQueue(&event);
}

/* Returns to the previous application*/

static void Uti_Exit(void)
{
EventType event;
MemSet(&event,sizeof(event),0);
event.eType = appStopEvent;
EvtAddEventToQueue(&event);
}

How to create a progress bar

/*********************/// Clear a progress bar/*******************************/

static void ProgressBarClear(UInt16 unTop,UInt16 unLeft,UInt16 unWidth)
{
Rect.topLeft.x=unLeft;
Rect.topLeft.y=unTop;
Rect.extent.x=unWidth;
Rect.extent.y=12;
WinEraseRectangle(&Rect,0);
}

/*************************/// Draw a progress bar /****************************/

static void ProgressBarInit(UInt16 unTop,UInt16 unLeft,UInt16 unWidth)
{
ProgressBarClear(unTop,unLeft,unWidth);
Rect.topLeft.x=unLeft+1;
Rect.topLeft.y=unTop+1;
Rect.extent.x=unWidth-2;
Rect.extent.y=10;
newRgb.r=0x00;
newRgb.g=0x00;
newRgb.b=0x00;
WinSetForeColorRGB(&newRgb,&amp;prevRgb);
WinDrawRectangleFrame(rectangleFrame,&Rect);
}

/***********************************/// ProgressBar
// Use example: Init with:ProgressBarInit(17,2,123);
// Call several time: ProgressBar(17,2,123,unIx,200);
// Clear with: ProgressBarClear(17,2,123);
/***************************************/

static void ProgressBar(UInt16 unTop,UInt16 unLeft,UInt16 unWidth,UInt32 unValue,UInt32 unMaxValue)
{
UInt32 ulTemp=0;
if(unMaxValue)
ulTemp=(unWidth-2)*unValue/unMaxValue;
if(ulTemp>(unWidth-2))
ulTemp=(unWidth-2);
Rect.topLeft.x=unLeft+1;
Rect.topLeft.y=unTop+1;
Rect.extent.x=ulTemp;
Rect.extent.y=10;
newRgb.r=0x00;
newRgb.g=0x00;
newRgb.b=0xFF;
WinSetForeColorRGB(&newRgb,&amp;prevRgb);
WinDrawRectangle(&Rect,0);
}

How to connect the palm to your phone, PC or other serial device through Bluetooth

Here is a simple way to communicate with your bluetooth serial device, this example code is as simple as possible you have to implement the errors management.You have to:

1) Load the Library
2) Find a device if not already known
3) Open the connection
4) Use it (Receive, Send, Flush, etc.)
5) Close the connection

/**************//* BT Globals *//*********************************************************/

static UInt16 unPortId;
static UInt16 btLibRefNum;
static UInt8 cAddress[6];
static Err err;
static SrmOpenConfigType config;
static BtVdOpenParams btParams;
static BtLibSdpUuidType sppUuid;

/*************************//* Pause n milliseconds *//************************************/

static void Uti_WaitMilliSec(UInt32 ulMilliSec)
{
UInt16 unTickPerSec;
unTickPerSec=SysTicksPerSecond();
if(unTickPerSec)
SysTaskDelay(ulMilliSec*unTickPerSec/1000);
else
SysTaskDelay(ulMilliSec/10);
}

/*********************//* Close a Bluetooth serial connetion *//*************************/

static void BT_Close()
{
if(unPortId)
{
SrmClose(unPortId);
unPortId=0;
Uti_WaitMilliSec(500);
SrmClose(unPortId); // Retry, on some system it's hard to die
}
}

/************************//* Open a Bluetooth serial connetion*//*********************/

static void BT_Open()
{
BT_Close();
MemSet(&sppUuid, sizeof(sppUuid), 0);
sppUuid.size = btLibUuidSize16;
sppUuid.UUID[0] = 0x11;
sppUuid.UUID[1] = 0x01;
MemSet(&btParams, sizeof(btParams), 0);
btParams.u.client.remoteDevAddr.address[0]=cAddress[0];
btParams.u.client.remoteDevAddr.address[1]=cAddress[1];
btParams.u.client.remoteDevAddr.address[2]=cAddress[2];
btParams.u.client.remoteDevAddr.address[3]=cAddress[3];
btParams.u.client.remoteDevAddr.address[4]=cAddress[4];
btParams.u.client.remoteDevAddr.address[5]=cAddress[5];
btParams.role = btVdClient;
btParams.u.client.method = btVdUseUuidList;
btParams.u.client.u.uuidList.tab = &sppUuid;
btParams.u.client.u.uuidList.len = 1;
MemSet(&config, sizeof(config), 0);
config.function = serFncUndefined;
config.drvrDataP = (MemPtr)&btParams;
config.drvrDataSize = sizeof(btParams);
err=SrmExtOpen(sysFileCVirtRfComm,&config,sizeof(config),&unPortId);
}

/**********************//* Find a BT device on air *//***************************/
static void BT_FindDevice()
{
BT_Close();
if(btLibRefNum)
{
err=BtLibOpen(btLibRefNum,false);
if(err==0)
err=BtLibDiscoverSingleDevice(btLibRefNum,NULL,NULL,0, (BtLibDeviceAddressType *) cAddress,false,true);
BtLibClose(btLibRefNum);
}
}

/***********************//* Load the BT library *//***********************/

static void BT_LoadLibrary()
{
btLibRefNum=0;
err=SysLibFind("Bluetooth Library",&btLibRefNum);
if(err)
err=SysLibLoad(sysFileTLibrary,sysFileCBtLib,&btLibRefNum);
}

/****************//* Flush BT ser *//****************/

static void BT_Flush(UInt16 unTimeout)
{
if(unPortId)
err=SrmReceiveFlush(unPortId,unTimeout);
}

/****************//* Send BT data *//****************/

static void BT_Send(char * pData,UInt16 unLen)
{
if(unPortId)
SrmSend(unPortId,pData,unLen,&err);
}

/*******************//* Receive BT data*//*******************/

static UInt16 BT_Receive(char * pData,UInt16 unLen,UInt16 unTimeout)
{
UInt16 unLenRead;
if(unPortId)
unLenRead=SrmReceive(unPortId,pData,unLen,unTimeout,&err);
return(unLenRead);
}

How to associate a file extension to a card directory

When you try to sync a file to the expansion card, often, you got an error saing that there is not a program associated to that file type

// For example to unregister the .txt extension write this
VFSUnregisterDefaultDirectory(".txt", expMediaType_Any);

// To associate the .txt extension to the /PALM/Laungher directory use this
VFSRegisterDefaultDirectory(".txt", expMediaType_Any, "/PALM/Launcher");

Drawing a Bitmap Family Member

This description is specific to Palm OS 3.5 and later, but for information on earlier Palm OS versions, see the [CompatNote compatibility notes] below.

# Use Constructor for Palm OS to open the form that will contain the bitmap.

# In the Project Window, create a new Bitmap Family.

# Open the Bitmap Family resource and set the values for the properties.

# Note that every member of the Bitmap Family must have the same Width and Height.

# You can set one of the colors to be transparent when the bitmap is drawn to the screen.

# You can add bitmaps of different depths to the bitmap family by selecting Edit > New Family Member(also by using Ctrl-K on Windows or Cmd-K on Macintosh). You can use up to five bitmap depths in one bitmap family, but bitmap images must be stored in increasing order of bit depth, and the bit depths must be unique. For backward compatibility, you should always have a 1-bit black and white bitmap as the first bitmap in the bitmap family.

# Do not add a Form Bitmap to the form. Rather, use the following code to draw the correct member of the bitmap family for the handheld's bit depth at runtime:

MemHandle hRsc = DmGetResource(bitmapRsc, TestBitmapFamily);

BitmapType* bitmapP = (BitmapType*) MemHandleLock(hRsc);

WinDrawBitmap(bitmapP, topLeftCoordX, topLeftCoordY);

MemHandleUnlock(hRsc);

DmReleaseResource(hRsc);

To improve the performance of drawing the bitmap, you can call the "Get" and "Lock" functions well in advance to calling the "Draw" function. In addition, if you plan to reuse the same bitmap, you can call the "Unlock" and "Release" functions later in your code. For example, you could load and lock a number of bitmaps in an AppStart function, and then unlock and release them in an AppStop function.

# Compatibility Notes

Prior to Palm OS 3.5, the steps for adding a bitmap were similar, but you would simply add a black-and-white bitmap rather than a bitmap family. For Palm OS 3.5 and later, using a bitmap family allows Palm OS to select the right bit depth that is supported for the screen hardware. If your version of Constructor has no support for Bitmap Family resources, then you need to upgrade to the version of Constructor that comes with Palm OS 4.0 SDK. Note that when you create a bitmap family in Constructor, it will look like each bitmap has its own object ID. However, these object IDs are for use by Constructor; your code cannot access an individual member of a bitmap family.

Fetching Unique ID of SD Card

You can call ExpCardInfo for the ID, but somehow the results differ:
m505: SD016_00016437230807,

T-T : SD016_000164372300D1
and even worse on a Handera 330:H330: SD016_00016437
The last 3 chars of m505 and T-T could be some kind of Checksum but whatabout the H330? This leads to the question:
How much of the numbers are significant? How much of these is the realCard-ID and whats irrelevant?


////////////////////////////////////////////////////////////////////////////////////////////

The last 4 digits of the card ID contains the date the card wasmanufactured. On the Tungsten T, there was some sort of byte ordering problem with this DWord, That explains why "0807" != "00D1" in your data above. Palm stated they will fix this problem on future devices, so that M- and T-class devices shall report the same number. This glitchrenders the last 4 digits insignificant.
The significant digits, then, are the last 8 hex chars after the *last* underscore are the card's serial number (exception: the last 2 charswill be 0x00 for old MMC cards). This serial number is required to be unique for a particular product line of cards, however this is not aglobal unique ID across all SD/MMC card vendors, or even across multiple models (product lines) of the same vendor. In other words, SanDisk could make a 512 Meg card with s/n 00000000, and then make a 1 GB cardwith the same s/n. To create a global unique ID that takes models & manufactures into account, one could hash the data before the *last* underscore, which is the card's manufacture and model number, and thenconcatenate it with the serial number.
I emphasize 'last' underscore above, because the manufacture's text ID(the first 5 bytes) could also contain an underscore.
So in your example above, "00016437" is the unique serial number of theSD card, the characters after this should be ignored, and "SD016" can beused to differentiate the model (product line).

Creating text with transparent background

Creating text with a transparent background is a problem that’s probably as old as the merry Palm OS function WinDrawChars. Many developers(including me) used abstruse techniques to create it(I used a bitmap lookup table, more on that if anyone is interested) - but I now found the official, PalmSource-approved, Palm TX safe way.

WinPushDrawState();
WinSetDrawMode(winOverlay);
WinPaintChar(char,xpos,ypos);
WinPopDrawState();

Thursday, August 16, 2007

Checking for New Features in Palm OS 5

Checking for OS Version ==================================================================
err = FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion);
if (romVersion >= sysMakeROMVersion(5,0,0,sysROMStageRelease,0)) //OS 5 or above


Pace Version
================================================================= You can check to see if you are running under the Palm Application Environment (PACE) and and at the same time get the version of PACE with:
FtrGet('pace', 0, &version);


Processor Type
================================================================
FtrGet(sysFtrCreator, sysFtrNumProcessorID, &procType);procType &= sysFtrNumProcessorMask;
You can then compare procType against the processor types defined in SysMgr.h
#define sysFtrNumProcessor328 0x00010000 // Motorola 68328 (Dragonball)

#define sysFtrNumProcessorEZ 0x00020000 // Motorola 68EZ328 (Dragonball EZ)
#define sysFtrNumProcessorVZ 0x00030000 // Motorola 68VZ328 (Dragonball VZ)
#define sysFtrNumProcessorSuperVZ 0x00040000 // Motorola 68SZ328 (Dragonball SuperVZ)
#define sysFtrNumProcessorARM720T 0x00100000 // ARM 720T
#define sysFtrNumProcessorARM7TDMI 0x00110000 // ARM7TDMI
#define sysFtrNumProcessorARM920T 0x00120000 // ARM920T
#define sysFtrNumProcessorARM922T 0x00130000 // ARM922T
#define sysFtrNumProcessorARM925 0x00140000 // ARM925
#define sysFtrNumProcessorStrongARM 0x00150000 // StrongARM
#define sysFtrNumProcessorXscale 0x00160000 // Xscale
#define sysFtrNumProcessorARM710A 0x00170000 // ARM710A
#define sysFtrNumProcessorx86 0x01000000 // Intel CPU (Palm Simulator)

High Density Display
===========================================================
To see if the High Density Feature Set is present, check the version of the Window Manager and confirm that it is 4 or greater:
err = FtrGet(sysFtrCreator, sysFtrNumWinVersion, &version);
Once you have confirmed that the High Density Feature Set is present, you can check the density of the screen with:
WinScreenGetAttribute(winScreenDensity, &attr);if (attr == kDensityDouble)//the screen is high density


Sampled Sound ==================================================================
err = FtrGet(sysFileCSoundMgr, sndFtrIDVersion, &version);
if(err)
{
// Sampled Sound Feature Set not present
}
else
{
// The Sampled Sound Feature Set is present.
// Check version number of Sound Manager, if necessary

if(version == expectedSndMgrVersionNum)
// everything is OK
}

SSL Library
=====================================================================
theError = SysLibFind("SslLib", &refNum);
if (theError)

theError = SysLibLoad(sysFileTLibrary, 'ssl0', &refNum);
if (theError)

//no SSL library
else
//SSL library is present

Crypto Library
================================================================
theError = SysLibFind("CPMlib", &refNum);
if (theError)

theError = SysLibLoad(sysFileTLibrary, 'cpml', &refNum);
if (theError)

//no crypto library
else
//crypto library is present