Wednesday, April 27, 2005

Pick-Me Buttons

Now that we knew how to draw transparently...


Pick-me button 1  Pick-me button 2  Pick-me button 3

What you see above is a group of pick-me buttons, which I so named because they seemed to jump up at me and say, "Pick me! Pick me!" Right now they're entirely pre-drawn, but eventually we'll be able to draw everything except the button image using RoundRect(), GradientFill(), and Ellipse().

One of my true passions has always been computer interfaces. How do we interact (link: see Usage Note) with these powerful machines that force us to work by their rules? But I'll spare you the soap box (in this post). What this meant for this project was that the entire user interface was my canvas, and I was an eager painter.

Pick-me buttons aren't really 2D or 3D controls. They live somewhere in the 2½D world. Their shadow gives them a sense of height and a different platform than the background they rest on. The illusion is compounded by the way that they jump up when selected, and their shadow correspondingly shrinks.

These buttons really only work in a horizontal arrangement, which makes them good if you have a landscape display. They use more space than their Win32 counterparts, toggle button and radio button groups. But in my opinion they're well worth the real estate. Also, for our application the only input was by a touch interface. Touching one of these buttons and seeing it spring to life gives immediate visual feedback and tactile gratification.

They worked even better when we set up our button class to reduce flicker by buffering its draws off-screen.

Drawing Transparent Controls

A quick search of the Windows CE newsgroups shows no small number of questions about transparency in controls. With its pared-down API, CE doesn't make it quite as easy as regular Win32 to draw transparently. Still, it can be done, and (as we found) it's not too difficult, either.

The biggest and foremost obstacle is, as the help puts it, that "all windows implicitly have the WS_CLIPSIBLINGS and WS_CLIPCHILDREN styles [set]." (In "regular" windows, a WM_PAINT message would cause a window to repaint its entire background. CE, an OS that usually runs on low-horsepower devices, tries to save some time by only drawing the region not covered by other windows. When a parent window is finished painting it selfishly tells all child windows to go paint themselves.) This works fine if you want to paint normal-looking buttons

WinCE Test Button

but as soon as you want to get fancy (move your mouse over the button to see the fancyness)

Oswald Test Button

it makes life a little harder.

In our case we wanted buttons with round corners. Also, they needed to depress and visibly lose their shadow (see above), which meant that if we merely painted the button image over itself we would still see the border from the last button image.

Also, many static images in our application needed a transparent background. (The alternative was to use icons, which don't scale well past 16x16 increments, or to "hard-code" the background into each image, which was a terrible option for obvious reasons.)

Enter our saviour, TransparentImage(), the CE equivalent of Win32's TransparentBlt() (or just blitting with a monochrome bit-mask).

Note: You might be tempted to just use SetBkMode(TRANSPARENT) in OnCtlColor. But the problem with doing that is that the control becomes too transparent and won't even repaint in response to a WM_ERASEBKGND message. On the other hand, passing back a brush created with the current background color would work just fine in OnCtlColor. You just have to make sure that you keep the brush valid (for example, make it a class member) or use a stock brush.

A note on my nomenclature: I use c_ as a prefix for control variables (to differentiate from m_ for data variables), a habit I picked up from the Flounder.

For drawing transparent static controls, this worked great in the parent window's OnPaint handler:
// Draw the "background" images transparently
// NOTE: Validate the control so it doesn't try to re-
// paint itself with its default bmp. Other options:
// 1. c_sensor.SetRedraw(false);
// 2. c_sensor.SetBitmap(bmp);
// 3. Subclass CStatic and paint our own control
c_sensor.ValidateRect(NULL);

pDC = c_sensor.GetDC();
pOldFont = pDC->SelectObject(&m_fontSmall);
c_sensor.GetClientRect(&rect1);
DrawBitmap(pDC, &rect1, IDB_SENSOR);

// Draw the centered text overlay on the bitmap
overlay.Format(_T("%d"), m_sensorNum);
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(overlay, &rect1, DT_CENTER);

pDC->SelectObject(pOldFont);
c_sensor.ReleaseDC(pDC);

DrawBitmap calls a parent-class function that calls TransparentImage(), like so:
TransparentImage(pDC->GetSafeHdc(), /* hdcDest */
pRect->left, pRect->top,
bmpInfo.bmWidth, bmpInfo.bmHeight, /* dest */
(HBITMAP) bmp, /* hdcSrc */
0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, /* src */
CLR_TRANSPARENT);

where CLR_TRANSPARENT is a pre-defined color set to hot pink, RGB(255, 0, 255). (I figured we wouldn't have much use for that color, and even if we did, I'd rather not see it!) For even more flexibility, you could also looked for a pre-determined pixel--say (0, 0)--and use its color as the images transparent color.

The most robust way to do this would be to use option #3 from above and subclass our own static controls. That way, we could control our background repainting in OnEraseBkgnd and our normal painting in DrawItem. But the statics were easy enough to handle in their parents' OnPaint method. We did subclass the buttons and used an off-screen buffer to do all our drawing before blitting them back over to the screen. But that's a post for another day.

Final note: You might look at our solution and say, "Hey, that's not really transparent. You're just faking it by drawing the same background color!" and you'd be right. This is good-enough transparency. It works well on a monochromatic background. True transparency involves using GetPixel() to see what's "underneath" and duplicating it, or at least part of it, in the draw. Yeah, that's also another post for another day.

Monday, April 25, 2005

Using a Custom SDK and Emulator with EVC

If you're not debugging on a physical device, you need a good emulator for testing code. In our case, our hardware spec still wasn't finished and a prototype wasn't close to being complete. But we did know that our 15" flat-panel display would run at 1024x768, which meant that the emulator default of 800x600 cut off a significant portion of our screen.

So we needed a custom emulator to run at that resolution. (We also needed it to handle the custom IOCTLs that we were sending, but that wouldn't come till later.) This shouldn't have been very hard to do, but as with all things just starting out, we didn't even know where to start troubleshooting because we didn't have a "known good" reference point.

Note: In my case, I had installed Platform Builder 4.2, then installed PB 5.0. When our SBC vendor told us that they only had a BSP for 4.2 I uninstalled version 5. This led to several wasted days of troubleshooting why coreroller.dll wasn't correctly registered, even after reinstalling v4.2. Moral of the story: don't try running 4.2 and 5.0 on the same system, as this search will attest to.

1. Make sure that you have a good platform and the Emulator Debug configuration is working.

Note: Make sure that in Platform Settings, for the emulator configuration, the Enable KITL option is unchecked! If you build a platform and export an SDK with this option turned on, eVC will not connect to the emulator unless Platform Builder is running and it can connect via KITL.

If you need to, rebuild the platform for that configuration.

2. Select Platform, Configure SDK... The SDK Roller will start in "Export SDK Wizard" mode. Run through the wizard and select the options for your platform. In our case we're using MFC for applications.

3. At the end of the wizard click Finish, but don't build the SDK yet. Select Platform, Configure SDK again, which now starts up the SDK Configuration Settings dialog. You may want to change some settings. For example, make sure that TCP/IP is the default transport. We wanted to mirror our device as closely as possible, so in the emulation tab we set the following:

  • Fixed screen resolution: width: 1024, height: 768, color: 16 (bits)
  • Memory: 64 (MB)

Note: If you plan on buildling multiple SDKs--we're on version 9 right now--you'll want to make sure to put the version number in the that the Platform name, and maybe even in the MSI file name setting. Otherwise, when you use Windows' uninstall applet, all of your SDKs in the uninstall list will have the exact same name.

4. When you've configured your settings, select Platform, Build SDK and watch it build (or work on something else if you're the industrious type). It should take a few minutes to build the MSI file and add the customized help, ending with this message:

MSI file construction completed. 0 error(s), 0 warning(s).

5. If you've been using eVC you should exit it now. The Platform Manager behaves strangely if you install an SDK while it's still running. Closing eVC usually terminates the Platform Manager as well, but after several bad experiences I always check-and-terminate cemgr.exe if it's still running. For good measure, I also exit Platform Builder.

6. Install the MSI file you created. It should take a few minutes, depending on which options you installed. When you restart eVC you'll see a new option in the Select Active WCE Configuration combo box of the WCE Platform toolbar (or select the Build, Set Active Platform menu). Rebuild your project under that platform and launch the emulator.

Note: If eVC fails to connect to the emulator you might need to reboot and restart eVC. Some people recommend always rebooting after installing an SDK, although I can usually get away without having to do that. If it still fails to connect, make sure that the KITL transport option is unchecked for your platform.

Another Note:The Platform Builder Help also has a step-by-step topic entitled Exporting an SDK.

Once we had our custom emulator up and running we never looked back. (For one thing, screen design was so much easier when we could see the entire screen!) Using the emulator made our development time so much faster. Even after we got our prototype, if we wanted to test the app, we would have to pull out the CF card from the hardware, copy the new executable to the card, put the card back into the prototype, reboot it, and run the app. Using the emulator meant that we could compile, download and run in a matter of seconds.

Last note: Compared with hardware, the emulator is of course slow, especially when debugging, but for quick testing it's more than adequate. Some of these eVC tips show how to get the most out of your emulator cycles.

Sunday, April 10, 2005

Thoughts Concerning the Instruction of Computing Machines (or, Methods to Programme Electronick Devices)

Dear Reader,

A curious piece of information has come to my attention that I thought most worthwhile to share with you. It appears that an astute portion of my readership has been following this tale with some interest, yet the details of my work are befuddling and torpor-inducing.

Therefore I thought it advantageous to share the proceƒs of my work, which I hope will enlighten the fellowship of readers. To my esteemed colleagues I ask leniancy while my path follows this slight detour, and now let us turn to the subject at hand.

Using an electronick computing machine I am able to manipulate a software programme, being a set of instructions for the computing machine, or computre, that charge its core with strict obedience, so that anon the same results are repeated.

The programme, which is so named Platfourme Builder, produces the desired result of specifying which components of a large system I should build. Certain components allow me certain functions--exempli gratia, a component known as Clear Type reveals the most wondrous movable type before my eyes.

Once my components are selected, I must only build this system, and using such built system I am able to package a reference of my system which would rival our dear Britannica. This reference is known as exporting the SDK, whose acronym is unknown to me but likely represents Scientific Device Kit.

Such SDK is then used in my IDE, known as eVC, for the purposes of writing specific instructions for my system. I apologise for the liberal use of acronyms when English words would suffice, but I find no other way of expreƒsing my language to you. What I call an IDE you may know as an integrated development environment, or a system for developing systems. Specifically, my environs is called by the name eVC, a system written in the third great instruction language (known as C) for embedded electronick devices.

When the instructions are properly built and ready--with nearly 20,000 operations at the present time and more being added daily it is a terrifyingly complex proceƒs--the system is run in a world which emulates ours. This other world, a sphere within a sphere, so to speak, is termed an emulatour, which purpose is to test our system before the instructions are run on an actual device.

Knowing that the instructions will work properly in the emulatour, I then place a copy of the instructions on the physikal device and attempt to monitor the electronick wizardry as it deciphers my commands.

From start to finish this is a most complicated proceƒs and at times days are spent sorting out the minutest nuance of our system. You may wonder if this is frustrating, and that is decidedly so, but it is also wondrous and fascinating, and the vexation of every impediment I encountre is but naught when compared to the delight of discovering its solution.

It is my desire that this discourse has been of some benefit to you.

I remain your most humble servant,
D. Philippe

Saturday, April 09, 2005

High-Speed Timers with Windows CE

A paradox that has always fascinated me about computers is the way that they do so much while being so stupid. At their heart computers have only a primitive bit of intelligence:
     is it a 1?
     is it a 0?
That's it. However, the redeeming quality of this neolithic process is the computer's ability to make more of these determinations in one second than you or I can make in our lifetimes. I've heard it called the dopeler effect: the tendency of dumb things to appear smarter when happen rapidly.

Primitive paradoxes aside, what this means in the real world is that computers work so well because they can do things amazingly faster than we can do them. Windows CE, for example, provides a timer that lets you track up to 1,000 things per second. For nearly all of the day-to-day programming tasks this is fast enough. But if you're working with high-speed machinery or programming servos on a robotic arm or, like us, watching cartons fly by at about 40 ft/sec (12 m/s) it's nowhere near fast enough.

For our purposes, we needed a timer that would fire 20,000 times per second, or about 50 µs. So, we stole the timer that Windows CE uses for the kernel profiler. It means that we lost any kernel profiling abillity but I don't know that we would've used it anyway.

Disclaimer: This is how we did it. This doesn't mean that it's the "best practices" way of doing it. (But boy, does it work!)

Setting up a high-speed timer isn't very hard. There are just three things to do. First, we need to modify timer.c. (Do make a backup.) For an X86 platform, we find this in %_WINCEROOT%\PUBLIC\COMMON\OAK\CSP\I486\OAL\timer.c. We'll make as few changes as possible.

// Our own interrupt task
extern void PolledInterruptTask(void);
...and later on...
int ProfileInterrupt(void) 
{
PolledInterruptTask();

dwReschedCount++;

if (dwReschedCount < dwProfilingMultiple)
return SYSINTR_NOP;

dwReschedCount = 0;
return SYSINTR_RESCHED;
}
Now we have a have an interrupt that will fire our custom task, whatever it is. We still need a way to start the profiler, which we can do using a custom IOCTL.

In oemctl.c we need to add the following case to OEMIoControl():
   switch (dwIoControlCode) { 
case IOCTL_START_CUSTOM_TIMER:
#ifndef DEBUG
// Start up the profiler timer
OEMProfileTimerEnable(50);
#endif

retval = TRUE;
break;
With the OAL parts working, we should build the platform. At this point we can also export a custom SDK to use with eVC (more on that in a later post).

Our last step is to call the new IOCTL as our application starts up. In our initialization code we might have something like this:
   if (!KernelIoControl(IOCTL_START_CUSTOM_TIMER,
NULL, 0, NULL, 0, NULL)) {
LOG(__FILE__, __LINE__,1,
"High-speed timer startup failed!");
}

It took us a little while to get everything working right, but once we did we tested it on a prototype and could verify that everything was working correctly. But was it running at 50 µs? It would be a little while before we could test it and find out.

Note 1: The MSDN lists a more in-depth way of implementing timers in CE in this article: Implementing Rock-Solid Windows CE Timers On Windows CE .NET 4.1 Platforms

Note 2: Modifying timer.c is also known as cloning the public code, which this MSDN article talks more about: Cloning the Microsoft Public Code