Time for GAPI

John Kennedy
Microsoft Corporation

November 14, 2001

View the source code for this article

I'm a big fan of games programming. I guess it's because I've spent so much time writing games for just about every computer platform that I've ever owned. Don't get me wrong, I'm not a professional games programmer, I just find the pastime an excellent way of getting to know hardware inside out.

When dealing with desktop Microsoft Windows® systems, programmers usually reach for DirectX®, the games application programming interface (API) library. DirectX has grown into the seminal tool for managing graphics and sound—and lots of other cool stuff too—but sadly, there is no version of DirectX for Windows CE. (Well, technically, that's not true. The Sega Dreamcast shipped with a version, but it's not available for Handheld or Pocket PCs.) With no custom hardware for supporting 3D graphics, no multi-channel sound synthesizer, and limited CPU and memory resources, porting a version of DirectX to the Pocket PC platform wasn't high on the CE developers' list of things to do.

The lack of a good DirectX API certainly presented a problem for those early adopters wishing to write games on the Pocket PC. Fast action games require fast screen updates. Fast screen updates require fast access to the screen memory. Unfortunately, accessing the screen memory on the wide range of hardware on the market was a painful and unreliable process. It was difficult to predict something as basic as the pattern of Red, Green, and Blue pixels on the LCD screen, or even if the screen was using color at all.

So, while a full DirectX port wasn't feasible, there was definitely a need for a device-independent way of getting speedy access to the screen. This is what GAPI—the Games API—is all about. GAPI provides a set of functions that provide direct read/write access to the screen buffer, and it first appeared on the Pocket PC back in April 2000. It's been so useful that it now comes included in the ROM of all the devices on the market, or at least all the ones I've seen anyway. To the programmer, it means all kinds of low level stuff is now possible. The OEMs of the individual Pocket PC devices are responsible for the tricky part (providing direct access to screen memory), leaving the programmer to develop the software, comfortable in the knowledge that things will behave in the same way on all devices.

GAPI Functions

The GAPI library consists of three files:

  • GX.H
  • GX.LIB
  • GX.DLL

The GX.DLL lives in the Windows/ directory and, as mentioned, is typically included in the ROM of most Pocket PC devices. The current version is 1.2, but older devices will have 1.0. You might need to include the latest DLL with your distributions to be sure your code is using the version you think it is.

The GAPI library isn't terribly large (less than 10 KB), mostly because it doesn't contain a vast library of code. Yes, it's fair to say GAPI is rather limited. It does provide the core functions you need, but not a lot else. There's no support for fancy 3D graphics. There's practically no support for 2D graphics, fancy or otherwise. Here's the complete list of what you get:

Structure Purpose
GXDisplayProperties This structure stores the key information about the Pocket PC's screen. Screen width, height, and pixel layout are stored here.
GXKeyList This structure stores information about the default hardware button assignments. This is GAPI's other purpose—making it possible to easily read button presses without accidentally launching the built-in PIM applications.
GXScreenRect Used internally by some GAPI functions.
Function Purpose
GXBeginDraw Prepares the display for writing. Similar to the good, old BeginDraw function we know and love, but it returns the hard-core video memory address, rather than the weaker HDC.
GXCloseDisplay Releases screen resources acquired by GAPI. Used at the end of a program.
GXCloseInput Releases button resources acquired by GAPI.
GXEndDraw Call this function after the main drawing code is finished—it's the matching half of GXBeginDraw. Forget to add it, and your device's memory will vanish in a few seconds.
GXGetDefaultKeys This useful function will return the recommended keys to use in a game. You can pass it a parameter to request either portrait (normal) or landscape (rotated 90 degrees) mode.
GXGetDisplayProperties Returns a GXDisplayProperties structure full of important information.
GXIsDisplayDRAMBuffer A GAPI v1.2 function that returns TRUE or FALSE if the display is "non standard." In practice, we don't care and can ignore this function.
GXOpenDisplay This vital function is called at the start of the program. Once invoked, GAPI will assume control over the display and prevent other programs from accessing it.
GXOpenInput Takes control over key presses, making sure they are all routed directly to this application's Windows message loop and not to the shell. This stops the user from launching any other applications while using your program. This can be very useful in all manner of situations.
GXResume Matched with a call to GXSuspend for temporarily disabling all the GAPI magic. It is good manners to use this following a WM_SETFOCUS message in your program.
GXSetViewport Another GAPI v1.2 function that you will probably never use.
GXSuspend Pauses all the GAPI stuff. This function should be called following a WM_KILLFOCUS message.

As you can see from the tables above, GAPI isn't exactly bursting with high-level functions such as DrawEnemySpacecraft or Draw3DView. However, if you've been bursting to write fast-action games on a Pocket PC, you will probably be excited at the possibilities offered. A typical Pocket PC has a speedy little processor, and with a nice, fast language like Visual C++® and direct access to screen memory, there's a lot of fun to be had. Not convinced? I've played a port of Quake on a Pocket PC. That good enough for ya?

Get Going with GAPI

A typical GAPI program isn't that different from any standard Windows CE program: you can even use the wizard generated Pocket PC source code as a basis for your GAPI programs. One catch: GAPI isn't supported on the SDK's Pocket PC emulator, so you'll need a real device for this code to work.

Before you start churning out the code, you'll need to download the latest GAPI archive from Microsoft's Web site.

You will need to copy the GX.H file into the Include directory of the Pocket PC SDK , and move the CPU-dependant GX.LIB files into the corresponding Lib directories. If you aren't convinced your device has the latest GX.DLL file, copy that to your Pocket PC's windows/ directory too.

Now you are ready to write your first GAPI software. Here's what to do:

  1. Create a new Pocket PC application in the eMbedded Visual C++ tool. Select the Hello World type program.

  2. From Project Settings, select the Link tab and make sure gx.lib is included.

  3. In order to use the GAPI functions, you'll need to add the line below to the start of your program:

    #include "gx.h"
    

    We'll also take this opportunity to declare some variables that GAPI will use to store all kinds of important stuff about the screen hardware. Add the following too:

    // Global GAPI variables:
    GXDisplayProperties gx_displayprop;
    GXKeyList gx_keylist;
    
  4. Then we add four GAPI function calls to the InitInstance function of our program. These calls will take over managing the screen and key inputs. Add the following after the call to UpdateWindows(hWnd):

       // GAPI Stuff
    
       // Attempt to take over the screen
       if (GXOpenDisplay( hWnd, GX_FULLSCREEN) == 0)
          return FALSE;
    
       // Get display properties
       gx_displayprop = GXGetDisplayProperties();
    
       // Take over button handling
       GXOpenInput();
    
       // Get default buttons for up/down etc.
       gx_keylist = GXGetDefaultKeys(GX_NORMALKEYS);
    
  5. As we've pretty much taken over the system at this point, we must be nice and give control back when the program exits. To do this, add the code below to the case WM_DESTROY in WinProc.:

       GXCloseInput();
       GXCloseDisplay();
    

If you compile and run the program now, you won't notice anything spectacularly different from the default. You'll simply have a program that says Hello World, but doesn't respond when you press any of the hardware keys. So far so good!

At this point, you might also want to comment out the CreateRpCommandBar and CommandBar_Destroy calls to stop the menu bar from appearing at the bottom of the screen. You might also be wondering how you can quit the program. To do this, go to Settings / Memory and select Running programs. Tap on your application and select Stop.

Adding Graphics to Your GAPI Application

With the screen of the Pocket PC under our complete control, now is the time to come to grips with actually drawing something. Remember when you first started programming with Windows CE and thought it irritating that you couldn't use MoveTo and LineTo, and needed to use PolyLine instead? Well, you are sure going to miss those days! Now that you've begged the Pocket PC to give you total access to the screen hardware, you don't even have a line drawing routine to play with yet.

Once you perform a GXBeginPaint call (no need to process WM_PAINT messages anymore), you have the address of the screen memory and can write values directly. The problem is, of course, what values do you actually write.

It turns out that Pocket PC devices support either 4 bits per pixel, 8 bits per pixel, or 16 bits per pixel. Most of the color devices on the market today support 16 bits per pixel, and for sake of simplicity, we'll assume this is the case for the sample code we'll develop. In fact, we'll add a condition just after the GXGetDisplayProperties call to make extra sure, like this:

// Get display properties
gx_displayprop = GXGetDisplayProperties();

// Check for 16 bit color display
if (gx_displayprop.cBPP != 16)
{
   // Only dealing with 16 bit color in this code
   GXCloseDisplay();
   MessageBox(hWnd,L"Sorry, only supporting 16bit color",L"Sorry!", MB_OK);
   return FALSE;
} 

There's another slight complication. Although the devices use 16 bits per pixel to store their color information, there are two variations in the way they use these 16 bits. I can hear you sighing now, but it's nothing too drastic. Peeking at a field of the GXDisplayProperties structure tells us how many of the bits are used to store blue information. It turns out that the most common answer is 6, but it doesn't do any harm to check for this at some point.

Armed with this information, we can then write a simple function that will clear the screen. When I say clear, I mean throw a lot of data into the screen buffer memory, thus overwriting what is already there. Here's the code.

// Clear the screen to a color described by the red, green, blue values
void ClearScreen(int r, int g, int b)
{

   // Get the start of the screen memory from the GX function.
   unsigned short * buffer  = (unsigned short *) GXBeginDraw();

   if (buffer == NULL) return;


   // Calculate the pixel color from the r,g,b components.
   unsigned short PixelCol;

   if (gx_displayprop.ffFormat & kfDirect565)
   {
      PixelCol = (unsigned short) (r & 0xff) << 11 | (g & 0xff) << 6 | (b & 0xff);
   }
   else
   {
      PixelCol = (unsigned short) (r & 0xff) << 10 | (g & 0xff) << 5 | (b & 0xff);
   }


   // Use two loops to fill the entire screen with the necessary color
   for (unsigned int y=0; y<gx_displayprop.cyHeight; y++)
   {
      unsigned short * pixel = buffer;

      for (unsigned int x=0; x<gx_displayprop.cxWidth; x++)
      {

         *pixel = PixelCol;
         pixel += gx_displayprop.cbxPitch >> 1;
      }

      buffer += gx_displayprop.cbyPitch >> 1;
   }

   // End the drawing code
   GXEndDraw();
}

Press the Button!

Other than drawing, GAPI provides a way of easily dealing with the hardware buttons. The calls to GXOpenInput and GXGetDefaultKeys prepare the system, and after that, all we need to do is add a little code to handle the WM_KEYDOWN message. Here's a sample that checks for the up key and exits the program if it has been pressed:

case WM_KEYDOWN:

if (wParam == (unsigned) gx_keylist.vkUp)
   {
      SendMessage(hWnd,WM_CLOSE,0,0);
   }
   break;

Pretty simple, eh? We can put all this together into a simple program that clears the screen to random colors using only a little extra code to set up a timer. The timer message in the WinProc function will call ClearScreen until the up button is pressed. Here's the WinProc from that program so you can see what it looks like:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

   switch (message) 
   {
      
      case WM_CREATE:
         SetTimer(hWnd,1,100,NULL);
         break;

      case WM_TIMER:
         ClearScreen(rand(),rand(),rand());
         break;

      case WM_KEYDOWN:
         if (wParam == (unsigned) gx_keylist.vkUp)
            SendMessage(hWnd,WM_CLOSE,0,0);
         break;

      case WM_DESTROY:
         KillTimer(hWnd,1);
         GXCloseInput();
         GXCloseDisplay();
         PostQuitMessage(0);
         break;

      case WM_KILLFOCUS:
         GXSuspend();
         break;

      case WM_SETFOCUS:
         GXResume();
         break;

      default:
         return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

Ready for Games?

Do you need to know anything more to write games? Actually, no. That quick look at GAPI and what we've covered in the sample program is enough to get some basic games working on your Pocket PC. If you're going to get serious about writing some entertainment software, you'll need to spend a some time working on a sprite routine (or porting that 3D voxel routine you found on the Internet), and playing with techniques, such as double-buffering and real-time WAV file mixing. And that, of course, is my excuse to leave the room and let you get on with it.

GAPI Isn't Just for Games

It's not only games programmers who will find GAPI useful. It turns out that many developers are finding it necessary to take control of a Pocket PC device, locking out all other applications. In an ideal world, the developer would be able to reprogram an existing Pocket PC, or create a unique device specifically for the chosen application. Unfortunately, creating a brand new operating system release for a Pocket PC isn't trivial (it's what Platform Builder is for, by the way), but the need for hardware-specific drivers makes this tricky, if not impossible for most developers.

GAPI can provide a useful way of giving the impression that the Pocket PC is running only the required program. The GXOpenInput function alone is worth the link with GX.DLL, and using GXOpenDisplay can make sure the poor souls using the bespoke application don't accidentally start playing solitaire or something equally awful.

Goodbye from GAPI

Until next month, here are a few links that will help you in your quest for GAPI information. Here's to hoping you can use it all to write some great games for what is an exciting little gaming platform!

GAPI for desktop Pocket PC emulator

It's unofficial, but it's a useful tool.

Getting started with GAPI

Here's a useful article on GAPI programming.

There are some great resources on this developer site.

Windows CE news

Beta Merlin SDK

The SDK for the Merlin/Pocket PC 2002 has finally been released. This is perhaps most useful for the updated emulator, but consider it an essential download.

SonicAdmin

Looking for a cool new toy to show your network administration? SonicAdmin allows you control your computers from anywhere you can get online. You can diagnose and fix mission critical server/workstation and network problems from your handheld wireless device.

 

John Kennedy is a Technical Writer/Programmer in the Visual C++ group by day and leads a secret life as a Pocket PC developer by night.

Larry Roof is a partner at tonked, a firm that specializes in the development of mobile solutions and training. He's the author of Professional Visual Basic Windows CE, available from Wrox Press.