Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
105 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

  Contents

 Introduction
 Some Win32
 API Calls

 Implementation
 References

 Download the
 Source

 Printable version

 


Implementation

OK. Now it's time to look at how we'd code that. I've designed a class called ResManager. It resides in a file called change.h and change.cpp. These files are meant to hold more than just the ResManager class, just in case you were wondering why the name of the file. Below is change.h (minus comments):

#include <stdio.h> #include <list.h> class ResManager { public: ResManager(); ResManager(int width, int height, int depth); ~ResManager(); void Init(); void ChangeRes(int width, int height, int depth); void RestoreResolution(); void ListResolutions(); private: DEVMODE m_Current; std::list<DEVMODE> m_stdDeviceModes; };

Pretty easy code here. Publicly, ResManager has two constructors, a default constructor (with no parameters) and a constructor that immediately switches the resolution. To switch the resolution at any time, make a call to ChangeRes. To restore the original resolution, make the call to RestoreResolution. ListResolutions will list (to the debug window) the available video modes.

Privately, we have the current video mode stored in m_Current. Also, we have an STL List container holding a list of available Device modes in m_stdDeviceModes.

How do we implement this? Let's go through the implementation of each method one at a time.

First, the default constructor:

ResManager::ResManager()
{
  int  nModeExist;
  DEVMODE devMode;
  devMode.dmSize = sizeof(DEVMODE);

  for (int i=0; ;i++) 
  {
    nModeExist = EnumDisplaySettings(NULL, i, 
        &devMode);

    if (nModeExist != 1) 
    {
      // End of modes.  bail out.
      break;
    }
    else
    {
      // Add the driver to the list.
        m_stdDeviceModes.push_front(devMode);
    }
  }
}

Simply put, we iterate through EnumDisplaySettings by incrementing i, until EnumDisplaySettings return a false. For each display mode we find, we add it to the list container m_stdDeviceModes.

Now, the parameterized constructor:

ResManager::ResManager(int width, int height, int depth)
{
  ResManager();
  ChangeRes(width, height, depth);
}

Editor's Note: The above code will not work as described. From Jason W:
The call to the default constructor in the above code fragment is creating a temporary object on the stack and then destroying it (since it isn't assigned to any variable). What you wanted to do was to have the code in the default constructor called under the current object's context. This cannot be done in C++. The way to do this is to have a common Init() function that is called from your different constructors.
This change has been made in the accompanying source code.

Ahh, the joy of C++. All we do is call the default constructor, and then ChangeRes. Easy as pie.

To change the resolution we need the following:

Popup : Source Listing ResManager::ChangeRes

Maybe a bit of explanation here. We need to run a couple of tests first. First off, we try to find the requested resolution from the available resolutions. If we fail, we should list out the available video modes to the user.

Assuming we got an available video mode, we then try to switch to it. First off, we try the easy approach, by doing a straight ChangeDisplaySettings. If that doesn't work, we then explicitly define the parameters that we want to use in changing the video resolution. If that doesn't work, we then try to change the Resolution, then the bit depth as separate calls. If those don't work, then nothing's going to work, and we bail.

To Restore the resolution is a simple matter. Here's the code:

void ResManager::RestoreResolution() { ChangeDisplaySettings(NULL, 0); }

And to List the Resolutions, we again do the iteration thing:

void ResManager::ListResolutions() { char buf[255]; std::list<DEVMODE>::iterator resIter; // Iterate through the DeviceModes list, // displaying a summary of each resIter = m_stdDeviceModes.begin(); sprintf(buf, "The following Resolutions are available:"); Log(LOG_MESSAGE, buf); while (resIter != m_stdDeviceModes.end()) { sprintf(buf, "Width: %d, Height:%d, BPP: %d", (*resIter).dmBitsPerPel, (*resIter).dmPelsWidth, (*resIter).dmPelsHeight); Log(LOG_MESSAGE, buf); resIter++; } }

And that, good folks, is a quick and dirty implementation of a non-DirectX based resolution switcher. The app that uses this is a console app that reads the command line, switches the resolution based on those arguments, and launches a program. The rezswitch program waits until the program has terminated, and then switches the resolution back to the original mode. How that program works isn't really related to game programming, so I'm going to skip it (that, and it's pretty rough around the edges as well). The project is a Visual Studio 6 project, so if anyone wants to do the conversion to other compilers (as I only have VC++ 6), feel free to do so.

OK. So that's it. Kind of a neat little programming exercise. What's next? Well, one of the spare-time projects that I'm working on is a 3D GUI system. You know, 3D windows, Buttons, scrollboxes. That kind of thing. Right now I'm in the design phase, and I might share some of that with you in a future article. But right now, it's still pretty raw.

Anyway, for questions, comments and/or issues you may have with the code, or just to shoot the breeze, please feel free to contact me at: ashleymatheson@hotmail.com.


Next : References