Выбрать главу

  hr = m_mousedev-›GetDeviceState(sizeof(DIMOUSESTATE), &dims);

  if (FAILED(hr)) {

   if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) {

    // device lost… reacquire

    hr = m_mousedev-›Acquire();

    if (FAILED(hr)) {

     // houston, we have a problem… clear & bail

     clear();

     done=1;

    }

   } else {

    // it’s some other error - clear and bail!

    m_mousedev.clear();

    done = 1;

   }

  } else // read mouse successfully!

  {

   done = 1;

  }

 } //while !done

 m_position.z += dims.lZ;

 if (m_vga-›isfullscreen()) {

  // we're in fullscreen, so this is easy… just copy the coords

  m_position.x += dims.lX;

  m_position.y += dims.lY;

 } else {

  // we're in window mode, so this is not-so-easy…

  // grab the relative mouse position

  GetCursorPos(&p);

  ScreenToClient((HWND)m_vga-›gethwnd(), &p);

  if (p.x ‹ 0 || p.y ‹ 0) {

   // the cursor is out of our window! "hide" it!

   m_mousedev.setposition(KJM_AXES_X, m_vga-›getscreendims().getwidth());

   m_mousedev.setposition(KJM_AXES_Y, m_vga-›getscreendims().getheight());

  } else {

   m_mousedev.setposition(KJM_AXES_X, p.x);

   m_mousedev.setposition(KJM_AXES_Y, p.y);

  }

 }

 m_mousedev.constrainpos(KJM_AXES_X, 0, m_vga-›getscreendims().getwidth());

 m_mousedev.constrainpos(KJM_AXES_Y, 0, m_vga-›getscreendims().getheight());

 for (q=0; q ‹ KJM_NUMMOUSEBUTTONS; q++) {

  m_mousedev.setbutton(q, (dims.rgbButtons[q] & 0x80));

 }

}

Part II: Windows

We’ll be using C++ heavily here. If you’re rusty on pure virtual functions, dynamic_cast’ing, etc., grab a C++ book and brush up before continuing.

The Design

Before we dive into code, it’s important to make a blueprint of what we’re aiming for.

In the finished GUI of our game, we’ll use a tree to keep track of every window displayed on the screen. The window tree is a simple n-node tree. At the root of the tree is the Windows Desktop (or, if you’re in X, the “root window” - now you know why they call it that). The children of the Desktop window are (usually) main windows; their children are dialog boxes, and the dialog boxes’ children are the individual dialog controls (buttons, textboxes, etc). An important distinction - the appearance of a window is NOT determined by its place in the tree. For example, many games place buttons directly on their desktop windows, as well as in dialogs.

And yes, buttons are windows too. This is a very important frame of mind. A button is just a window with a funny appearance. In fact, all of the GUI controls are simply windows with different appearances. This is where the power of C++ comes in. If we create a generic window class, and give it a few virtual functions, we can then easily create our different controls by overriding the base window class’s behavior. This use of polymorphism is extremely elegant; so elegant, in fact, that many C++ books use it as an example. (I’ll talk more about this in Part III.)

That’s our basic design, now, let’s work through an implementation strategy…

The Plan

I took the following steps when I implemented my GUI:

1) First I coded some basic window management code. This chunk of code is responsible for the window tree, adding / deleting windows (i.e., new’ing and deleting window pointers), showing / hiding them, moving them to the top of the Z-Order, etc. I stubbed out the window drawing procedure by simply drawing rectangles where my windows should be, then drawing a number in the top-left corner of them to indicate their z-order.

Understand up front that your life will become tremendously easier if you buy or make a good, solid, template class for arrays of pointers. The STL (Standard Template Library) that ships with most versions of C++ has several good template-able pointer array classes, but if you want to make your own, do it formally - test it thoroughly and completely before you start implementing your window manager. The last things you need right now are subtle memory leaks or null pointer references caused by a shoddy array class.

2) Once I had basic window management functions, I spent some time thinking about my coordinate systems. Coded up some ClientToScreen() functions, and some other misc. stuff.

3) Next, I tackled the window drawing code. I derived a “fancy window” class, and showed it how to draw itself using a set of nine sprites - four sprites for the corners, four sprites for the edges, and one sprite for the background (see diagram) ‹‹DIAGRAM››.

Using nine window sprites, it’s possible to create windows that sport a unique, artistic appearance, and yet are still dynamically re-sizeable (ala StarDock’s WindowBlinds). The downside to this is that you’ll need a fairly smart drawing library, one that can handle tiling sprites, stretching them, and centering them, as well as a very complex window creation program (something the artists can use to construct their windows), to really make this method work well. And, of course, you’ll pay in window drawing speed, too.

4) Once the drawing code for the generic window was complete, I started implementing the controls. Coding controls is straightforward, but again, requires very thorough testing. I started with the simple controls: statics, icons, etc., and worked my way up from there, as explained earlier.

5) Finally, after all of my controls were complete, I coded up a simple Resource Editor, a program that allows someone to graphically place controls and layout dialog boxes. The resource editor took me a good month to do, but I highly suggest doing it (instead of just using text files to position stuff) - it’s much easier to create dialog boxes graphically, and it was a good exercise: during development I uncovered several bugs in my controls’ code, things that would have proven very difficult to catch in the actual game.

I toyed, for a very long time, with the idea of creating a program that would convert an MSVC++ resource (.RC) file into a custom resource file useable by my GUI. In the end, I decided such a program would be more trouble than what it would be worth. The whole reason I was writing a GUI was to get away from the confines of Windows, and to truly do that, I needed my own editor, tied to my own resource file format and my own way of doing things. I decided to implement a WYSIWYG Resource Editor in MFC from the ground up. My needs, my decision; your needs may be different. If anyone out there tries to write a converter, I’d love to hear about it.

So… let’s start with step one: basic window management functions.

The Implementation

Here we go. Here’s a good start for our base-class window definition:

class gui_window {

public: