]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/display.cpp
fixes needed for separate DLL build to work
[wxWidgets.git] / src / msw / display.cpp
index cd5548c708144c58f7576c582877b32564e6cace..10012b320f6b21406889f0c4347bc66a68de6ec6 100644 (file)
@@ -2,7 +2,7 @@
 // Name:        display.cpp
 // Purpose:     MSW Implementation of wxDisplay class
 // Author:      Royce Mitchell III
-// Modified by:
+// Modified by: VZ (resolutions enumeration/change support, DirectDraw, ...)
 // Created:     06/21/02
 // RCS-ID:      $Id$
 // Copyright:   (c) wxWindows team
    #include "wx/dynarray.h"
 #endif
 
+#include "wx/dynload.h"
+
 #include "wx/display.h"
 
 // the following define is necessary to access the multi-monitor function
 // declarations in a manner safe to use w/ Windows 95
-// JACS: not used for now until we're clear about the legality
-// of distributing multimon.h. Meanwhile you can download the file
-// yourself from:
-// http://www.microsoft.com/msj/0697/monitor/monitortextfigs.htm#fig4
-
-#if 0
 #define COMPILE_MULTIMON_STUBS
-#include "wx/msw/multimon.h"
+
+// if you don't have multimon.h you can download the file from:
+//
+//  http://www.microsoft.com/msj/0697/monitor/monitortextfigs.htm#fig4
+//
+
+#ifdef _MSC_VER
+    // as (m)any standard header(s), this one doesn't compile without warnings
+    // with VC++ 6 <sigh>
+    #pragma warning(disable:4706)
 #endif
 
-// ---------------------------------------------------------------------------
-// constants
-// ---------------------------------------------------------------------------
+#include <multimon.h>
 
-// ---------------------------------------------------------------------------
-// private functions
-// ---------------------------------------------------------------------------
+#ifdef _MSC_VER
+    #pragma warning(default:4706)
+#endif
+
+#include <ddraw.h>
 
-void wxmswInitDisplayRectArray();
+// we don't want to link with ddraw.lib which contains the real
+// IID_IDirectDraw2 definition
+const GUID wxIID_IDirectDraw2 =
+    { 0xB3A6F3E0, 0x2B43, 0x11CF, { 0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 } };
 
 // ----------------------------------------------------------------------------
-// private classes
+// macros
 // ----------------------------------------------------------------------------
 
-class wxmswDisplayInfo;
+#ifdef _UNICODE
+    #define WINFUNC(x)  _T(#x) L"W"
+#else
+    #define WINFUNC(x) #x "A"
+#endif
+
+// ----------------------------------------------------------------------------
+// typedefs for dynamically loaded Windows functions
+// ----------------------------------------------------------------------------
 
-WX_DECLARE_OBJARRAY(wxmswDisplayInfo, wxmswDisplayInfoArray);
+typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName,
+                                                 LPDEVMODE lpDevMode,
+                                                 HWND hwnd,
+                                                 DWORD dwFlags,
+                                                 LPVOID lParam);
 
-class wxmswDisplayInfo
+typedef BOOL (PASCAL *DDEnumExCallback_t)(GUID *pGuid,
+                                          LPTSTR driverDescription,
+                                          LPTSTR driverName,
+                                          LPVOID lpContext,
+                                          HMONITOR hmon);
+
+typedef HRESULT (WINAPI *DirectDrawEnumerateEx_t)(DDEnumExCallback_t lpCallback,
+                                                  LPVOID lpContext,
+                                                  DWORD dwFlags);
+
+typedef HRESULT (WINAPI *DirectDrawCreate_t)(GUID *lpGUID,
+                                             LPDIRECTDRAW *lplpDD,
+                                             IUnknown *pUnkOuter);
+
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+class wxDisplayInfo
 {
 public:
-       HMONITOR m_hmon;
-    DISPLAY_DEVICE m_dd;
+    // handle of this monitor used by MonitorXXX() functions, never NULL
+    HMONITOR m_hmon;
+
+    // IDirectDraw object used to control this display, may be NULL
+    IDirectDraw2 *m_pDD2;
+
+    // DirectDraw GUID for this display, only valid when using DirectDraw
+    GUID m_guid;
+
+    // the entire area of this monitor in virtual screen coordinates
     wxRect m_rect;
-    int m_depth;
+
+    // the display device name for this monitor, empty initially and retrieved
+    // on demand by DoGetName()
+    wxString m_devName;
+
+    wxDisplayInfo() { m_hmon = NULL; m_pDD2 = NULL; }
+    ~wxDisplayInfo() { if ( m_pDD2 ) m_pDD2->Release(); }
 };
 
-wxmswDisplayInfoArray* g_wxmswDisplayInfoArray = 0;
+WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray);
+#include "wx/arrimpl.cpp"
+WX_DEFINE_OBJARRAY(wxDisplayInfoArray);
 
-#include <wx/arrimpl.cpp> // this is a magic incantation which must be done!
-WX_DEFINE_OBJARRAY(wxmswDisplayInfoArray);
+// this module is used to cleanup gs_displays array
+class wxDisplayModule : public wxModule
+{
+public:
+    virtual bool OnInit() { return true; }
+    virtual void OnExit();
+
+    DECLARE_DYNAMIC_CLASS(wxDisplayModule)
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule, wxModule)
+
+// ----------------------------------------------------------------------------
+// globals
+// ----------------------------------------------------------------------------
+
+// do we use DirectX?
+static bool gs_useDirectX = false;
+
+// dynamically resolved DirectDrawCreate()
+static DirectDrawCreate_t gs_DirectDrawCreate = NULL;
+
+// this is not really MT-unsafe as wxDisplay is only going to be used from the
+// main thread, i.e. we consider that it's a GUI class and so don't protect it
+static wxDisplayInfoArray *gs_displays = NULL;
 
 // ===========================================================================
 // implementation
 // ===========================================================================
 
-BOOL CALLBACK wxmswMonitorEnumProc (
+// ----------------------------------------------------------------------------
+// callbacks for monitor/modes enumeration stuff
+// ----------------------------------------------------------------------------
+
+static BOOL CALLBACK wxmswMonitorEnumProc (
   HMONITOR hMonitor,  // handle to display monitor
-  HDC hdcMonitor,     // handle to monitor-appropriate device context
+  HDC hdcMonitor,     // handle to monitor-appropriate device context (NULL)
   LPRECT lprcMonitor, // pointer to monitor intersection rectangle
-  LPARAM dwData       // data passed from EnumDisplayMonitors
+  LPARAM dwData       // data passed from EnumDisplayMonitors (unused)
 )
 {
-    wxmswDisplayInfo* info = new wxmswDisplayInfo();
+    wxDisplayInfo *info = new wxDisplayInfo();
+
+    // we need hMonitor to be able to map display id to it which is needed for
+    // MonitorXXX() functions, in particular MonitorFromPoint()
     info->m_hmon = hMonitor;
+
+    // we also store the display geometry
     info->m_rect.SetX ( lprcMonitor->left );
     info->m_rect.SetY ( lprcMonitor->top );
     info->m_rect.SetWidth ( lprcMonitor->right - lprcMonitor->left );
     info->m_rect.SetHeight ( lprcMonitor->bottom - lprcMonitor->top );
+
     // now add this monitor to the array
-    g_wxmswDisplayInfoArray->Add ( info );
+    gs_displays->Add(info);
 
-    return TRUE; // continue the enumeration
+    // continue the enumeration
+    return true;
 }
 
-class wxmswDisplayModule : public wxModule
+BOOL PASCAL
+wxDDEnumExCallback(GUID *pGuid,
+                   LPTSTR driverDescription,
+                   LPTSTR driverName,
+                   LPVOID lpContext,
+                   HMONITOR hmon)
 {
-    DECLARE_DYNAMIC_CLASS(wxmswDisplayModule)
-public:
-    wxmswDisplayModule() {}
-    bool OnInit();
-    void OnExit();
-};
+    if ( pGuid )
+    {
+        wxDisplayInfo *info = new wxDisplayInfo();
+
+        info->m_hmon = hmon;
+        info->m_guid = *pGuid;
+        info->m_devName = driverName;
+
+        gs_displays->Add(info);
+    }
+    //else: we're called for the primary monitor, skip it
 
-IMPLEMENT_DYNAMIC_CLASS(wxmswDisplayModule, wxModule)
+    // continue the enumeration
+    return true;
+}
 
-bool wxmswDisplayModule::OnInit()
+HRESULT WINAPI wxDDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc,  
+                                     LPVOID lpContext)
 {
-    g_wxmswDisplayInfoArray = new wxmswDisplayInfoArray();
-    if ( !g_wxmswDisplayInfoArray )
+    // we need at least the mode size
+    static const int FLAGS_REQUIRED = DDSD_HEIGHT | DDSD_WIDTH;
+    if ( (lpDDSurfaceDesc->dwFlags & FLAGS_REQUIRED) == FLAGS_REQUIRED )
     {
-        wxFAIL_MSG(wxT("Couldn't allocate array for display information"));
-        return FALSE;
+        wxArrayVideoModes * const modes = (wxArrayVideoModes *)lpContext;
+
+        modes->Add(wxVideoMode(lpDDSurfaceDesc->dwWidth,
+                               lpDDSurfaceDesc->dwHeight,
+                               lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount,
+                               lpDDSurfaceDesc->dwRefreshRate));
     }
 
-    // Royce3: I'm assuming that the monitor's are enumerated in the same
-    // order as the calls to EnumDisplayDevices below. We shall soon see
-    // if that assumption is correct.
-    if ( !EnumDisplayMonitors ( NULL, NULL, wxmswMonitorEnumProc, 0 ) )
-        wxLogLastError(wxT("EnumDisplayMonitors"));
+    // continue the enumeration
+    return DDENUMRET_OK;
+}
 
-    size_t iDevNum = 0, count = g_wxmswDisplayInfoArray->Count();
-    while ( iDevNum < count )
-    {
-        wxmswDisplayInfo& info = (*g_wxmswDisplayInfoArray)[iDevNum];
+// ----------------------------------------------------------------------------
+// local functions
+// ----------------------------------------------------------------------------
 
-        // MSDN: Before calling EnumDisplayDevices, you must initialize the cb
-        // member of DISPLAY_DEVICE to the size, in bytes, of DISPLAY_DEVICE
-        info.m_dd.cb = sizeof(info.m_dd);
+// initialize gs_displays using DirectX functions
+static bool DoInitDirectX()
+{
+    // suppress the errors if ddraw.dll is not found
+    wxLog::EnableLogging(false);
 
-        if ( !EnumDisplayDevices ( NULL, iDevNum, &info.m_dd, 0 ) )
-            wxLogLastError(wxT("EnumDisplayDevices"));
+    wxDynamicLibrary dllDX(_T("ddraw.dll"));
 
-        // get this display's Depth
-        DEVMODE devmode;
-        memset ( &devmode, 0, sizeof(devmode) );
+    wxLog::EnableLogging(true);
 
-        // MSDN: Before calling EnumDisplaySettings, set the dmSize member to
-        // sizeof(DEVMODE), and set the dmDriverExtra member to indicate the size,
-        // in bytes, of the additional space available to receive private
-        // driver-data.
-        devmode.dmSize = sizeof(devmode);
-        devmode.dmDriverExtra = 0;
+    if ( !dllDX.IsLoaded() )
+        return false;
 
-        if ( !EnumDisplaySettings ( info.m_dd.DeviceName, ENUM_CURRENT_SETTINGS, &devmode ) )
-        {
-            wxLogLastError(wxT("EnumDisplaySettings"));
-            devmode.dmFields = 0;
-        }
+    DirectDrawEnumerateEx_t pDDEnumEx = (DirectDrawEnumerateEx_t)
+        dllDX.GetSymbol(WINFUNC(DirectDrawEnumerateEx));
+    if ( !pDDEnumEx )
+        return false;
 
-        if ( !(devmode.dmFields&DM_BITSPERPEL) )
-            info.m_depth = -1;
-        else
-            info.m_depth = devmode.dmBitsPerPel;
+    // we'll also need DirectDrawCreate() later, resolve it right now
+    gs_DirectDrawCreate = (DirectDrawCreate_t)
+        dllDX.GetSymbol(_T("DirectDrawCreate"));
+    if ( !gs_DirectDrawCreate )
+        return false;
+
+    if ( (*pDDEnumEx)(wxDDEnumExCallback,
+                      NULL,
+                      DDENUM_ATTACHEDSECONDARYDEVICES) != DD_OK )
+    {
+        return false;
+    }
 
+    // ok, it seems like we're going to use DirectDraw and so we're going to
+    // need ddraw.dll all the time, don't unload it
+    dllDX.Detach();
 
-        iDevNum++;
+    return true;
+}
+
+// initialize gs_displays using the standard Windows functions
+static void DoInitStdWindows()
+{
+    // enumerate all displays
+    if ( !::EnumDisplayMonitors(NULL, NULL, wxmswMonitorEnumProc, 0) )
+    {
+        wxLogLastError(wxT("EnumDisplayMonitors"));
+
+        // TODO: still create at least one (valid) entry in gs_displays for the
+        //       primary display!
     }
-    return TRUE;
 }
 
-void wxmswDisplayModule::OnExit()
+// this function must be called before accessing gs_displays array as it
+// creates and initializes it
+static void InitDisplays()
 {
-    size_t count = g_wxmswDisplayInfoArray->Count();
-    while ( count-- )
+    if ( gs_displays )
+        return;
+
+    gs_displays = new wxDisplayInfoArray();
+
+    if ( !gs_useDirectX || !DoInitDirectX() )
     {
-        wxmswDisplayInfo* info = g_wxmswDisplayInfoArray->Detach ( count );
-        delete info;
+        // either we were told not to try to use DirectX or fall back to std
+        // functions if DirectX method failed
+        gs_useDirectX = false;
+
+        DoInitStdWindows();
     }
-    delete g_wxmswDisplayInfoArray;
-    g_wxmswDisplayInfoArray = 0;
+}
+
+// convert a DEVMODE to our wxVideoMode
+wxVideoMode ConvertToVideoMode(const DEVMODE& dm)
+{
+    // note that dmDisplayFrequency may be 0 or 1 meaning "standard one" and
+    // although 0 is ok for us we don't want to return modes with 1hz refresh
+    return wxVideoMode(dm.dmPelsWidth,
+                       dm.dmPelsHeight,
+                       dm.dmBitsPerPel,
+                       dm.dmDisplayFrequency > 1 ? dm.dmDisplayFrequency : 0);
+}
+
+// emulation of ChangeDisplaySettingsEx() for Win95
+LONG WINAPI ChangeDisplaySettingsExForWin95(LPCTSTR WXUNUSED(lpszDeviceName),
+                                            LPDEVMODE lpDevMode,
+                                            HWND WXUNUSED(hwnd),
+                                            DWORD dwFlags,
+                                            LPVOID WXUNUSED(lParam))
+{
+    return ::ChangeDisplaySettings(lpDevMode, dwFlags);
+}
+
+// ----------------------------------------------------------------------------
+// wxDisplayModule
+// ----------------------------------------------------------------------------
+
+void wxDisplayModule::OnExit()
+{
+    delete gs_displays;
 }
 
 // ---------------------------------------------------------------------------
 // wxDisplay
 // ---------------------------------------------------------------------------
 
+/* static */
+void wxDisplay::UseDirectX(bool useDX)
+{
+    wxCHECK_RET( !gs_displays, _T("it is too late to call UseDirectX") );
+
+    gs_useDirectX = useDX;
+}
+
+// helper of GetFromPoint() and GetFromWindow()
+static int DisplayFromHMONITOR(HMONITOR hmon)
+{
+    if ( hmon )
+    {
+        const size_t count = wxDisplay::GetCount();
+
+        for ( size_t n = 0; n < count; n++ )
+        {
+            if ( hmon == (*gs_displays)[n].m_hmon )
+                return n;
+        }
+    }
+
+    return wxNOT_FOUND;
+}
+
+/* static */
 size_t wxDisplayBase::GetCount()
 {
-    return GetSystemMetrics ( SM_CMONITORS );
+    InitDisplays();
+
+    // I'm not sure if they really always return the same thing and if this is
+    // not true I'd like to know in which situation does it happen
+    wxASSERT_MSG( gs_displays->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS),
+                    _T("So how many displays does this system have?") );
+
+    return gs_displays->GetCount();
 }
 
+/* static */
 int wxDisplayBase::GetFromPoint ( const wxPoint& pt )
 {
     POINT pt2;
     pt2.x = pt.x;
     pt2.y = pt.y;
 
-    HMONITOR hmon = MonitorFromPoint ( pt2, 0 );
-    if ( !hmon )
-        return -1;
-    size_t count = wxDisplayBase::GetCount(), index;
+    return DisplayFromHMONITOR(::MonitorFromPoint(pt2, MONITOR_DEFAULTTONULL));
+}
+
+/* static */
+int wxDisplayBase::GetFromWindow(wxWindow *window)
+{
+    return DisplayFromHMONITOR
+           (
+            ::MonitorFromWindow(GetHwndOf(window), MONITOR_DEFAULTTONULL)
+           );
+}
+
+// ----------------------------------------------------------------------------
+// wxDisplay ctor/dtor
+// ----------------------------------------------------------------------------
+
+wxDisplay::wxDisplay ( size_t n )
+         : wxDisplayBase ( n )
+{
+    // if we do this in ctor we won't have to call it from all the member
+    // functions
+    InitDisplays();
 
-    for ( index = 0; index < count; index++ )
+    if ( gs_useDirectX )
     {
-        if ( hmon == (*g_wxmswDisplayInfoArray)[index].m_hmon )
-            return index;
+        wxDisplayInfo& dpyInfo = (*gs_displays)[n];
+
+        LPDIRECTDRAW2& pDD2 = dpyInfo.m_pDD2;
+        if ( !pDD2 )
+        {
+            if ( !gs_DirectDrawCreate )
+            {
+                // what to do??
+                return;
+            }
+
+            IDirectDraw *pDD;
+            HRESULT hr = (*gs_DirectDrawCreate)(&dpyInfo.m_guid, &pDD, NULL);
+
+            if ( FAILED(hr) || !pDD )
+            {
+                // what to do??
+                wxLogApiError(_T("DirectDrawCreate"), hr);
+            }
+            else // got IDirectDraw, we want IDirectDraw2
+            {
+                hr = pDD->QueryInterface(wxIID_IDirectDraw2, (void **)&pDD2);
+                if ( FAILED(hr) || !pDD2 )
+                {
+                    wxLogApiError(_T("IDirectDraw::QueryInterface(IDD2)"), hr);
+                }
+
+                pDD->Release();
+            }
+        }
+        //else: DirectDraw object corresponding to our display already exists
+
+        // increment its ref count to account for Release() in dtor
+        //
+        // NB: pDD2 will be only really Release()d when gs_displays is
+        //     destroyed which is ok as we don't want to recreate DD objects
+        //     all the time
+        pDD2->AddRef();
     }
+}
 
-    return -1;
+wxDisplay::~wxDisplay()
+{
+    wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
+
+    LPDIRECTDRAW2& pDD2 = dpyInfo.m_pDD2;
+    if ( pDD2 )
+    {
+        pDD2->Release();
+    }
 }
 
-wxDisplay::wxDisplay ( size_t index ) : wxDisplayBase ( index )
+// ----------------------------------------------------------------------------
+// wxDisplay simple accessors
+// ----------------------------------------------------------------------------
+
+bool wxDisplay::IsOk() const
 {
+    return m_index < GetCount() &&
+                (!gs_useDirectX || (*gs_displays)[m_index].m_pDD2);
 }
 
 wxRect wxDisplay::GetGeometry() const
 {
-    return (*g_wxmswDisplayInfoArray)[m_index].m_rect;
+    wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
+    wxRect& rect = dpyInfo.m_rect;
+    if ( !rect.width )
+    {
+        MONITORINFO monInfo;
+        wxZeroMemory(monInfo);
+        monInfo.cbSize = sizeof(monInfo);
+
+        if ( !::GetMonitorInfo(dpyInfo.m_hmon, &monInfo) )
+        {
+            wxLogLastError(_T("GetMonitorInfo"));
+        }
+        else
+        {
+            wxCopyRECTToRect(monInfo.rcMonitor, rect);
+        }
+    }
+
+    return rect;
 }
 
-int wxDisplay::GetDepth() const
+wxString wxDisplay::GetName() const
 {
-    return (*g_wxmswDisplayInfoArray)[m_index].m_depth;
+    wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
+    if ( dpyInfo.m_devName.empty() )
+    {
+        MONITORINFOEX monInfo;
+        wxZeroMemory(monInfo);
+        monInfo.cbSize = sizeof(monInfo);
+
+        if ( !::GetMonitorInfo(dpyInfo.m_hmon, &monInfo) )
+        {
+            wxLogLastError(_T("GetMonitorInfo"));
+        }
+        else
+        {
+            dpyInfo.m_devName = monInfo.szDevice;
+        }
+    }
+
+    return dpyInfo.m_devName;
 }
 
-wxString wxDisplay::GetName() const
+wxString wxDisplay::GetNameForEnumSettings() const
+{
+    int major, minor;
+    const bool isWin95 = wxGetOsVersion(&major, &minor) == wxWIN95 &&
+                            major == 4 && minor == 0;
+
+    // the first parameter of EnumDisplaySettings() must be NULL under Win95
+    // according to MSDN but GetMonitorInfo() stub in multimon.h still returns
+    // something even in this case, so we have to correct this manually
+    wxString name;
+    if ( !isWin95 )
+        name = GetName();
+
+    return name;
+}
+
+// ----------------------------------------------------------------------------
+// video modes enumeration
+// ----------------------------------------------------------------------------
+
+wxArrayVideoModes
+wxDisplay::DoGetModesDirectX(const wxVideoMode& modeMatch) const
+{
+    wxArrayVideoModes modes;
+
+    IDirectDraw2 *pDD = (*gs_displays)[m_index].m_pDD2;
+
+    if ( pDD )
+    {
+        HRESULT hr = pDD->EnumDisplayModes
+                          (
+                            DDEDM_REFRESHRATES,
+                            NULL,   // all modes (TODO: use modeMatch!)
+                            &modes,   // callback parameter
+                            wxDDEnumModesCallback
+                          );
+
+        if ( FAILED(hr) )
+        {
+            wxLogApiError(_T("IDirectDraw::EnumDisplayModes"), hr);
+        }
+    }
+
+    return modes;
+}
+
+wxArrayVideoModes
+wxDisplay::DoGetModesWindows(const wxVideoMode& modeMatch) const
+{
+    wxArrayVideoModes modes;
+
+    const wxString name = GetNameForEnumSettings();
+
+    const wxChar * const deviceName = name.empty() ? NULL : name.c_str();
+
+    DEVMODE dm;
+    for ( int iModeNum = 0;
+          ::EnumDisplaySettings(deviceName, iModeNum, &dm);
+          iModeNum++ )
+    {
+        const wxVideoMode mode = ConvertToVideoMode(dm);
+        if ( mode.Matches(modeMatch) )
+        {
+            modes.Add(mode);
+        }
+    }
+
+    return modes;
+}
+
+wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const
+{
+    return gs_useDirectX ? DoGetModesDirectX(modeMatch)
+                         : DoGetModesWindows(modeMatch);
+}
+
+wxVideoMode wxDisplay::GetCurrentMode() const
+{
+    wxVideoMode mode;
+
+    const wxString name = GetNameForEnumSettings();
+
+    DEVMODE dm;
+    if ( !::EnumDisplaySettings(name.empty() ? NULL : name.c_str(),
+                                ENUM_CURRENT_SETTINGS,
+                                &dm) )
+    {
+        wxLogLastError(_T("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
+    }
+    else
+    {
+        mode = ConvertToVideoMode(dm);
+    }
+
+    return mode;
+}
+
+// ----------------------------------------------------------------------------
+// video mode switching
+// ----------------------------------------------------------------------------
+
+bool wxDisplay::DoChangeModeDirectX(const wxVideoMode& mode)
+{
+    IDirectDraw2 *pDD = (*gs_displays)[m_index].m_pDD2;
+    if ( !pDD )
+        return false;
+
+    wxWindow *winTop = wxTheApp->GetTopWindow();
+    wxCHECK_MSG( winTop, false, _T("top level window required for DirectX") );
+
+    HRESULT hr = pDD->SetCooperativeLevel
+                      (
+                        GetHwndOf(winTop),
+                        DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN
+                      );
+    if ( FAILED(hr) )
+    {
+        wxLogApiError(_T("IDirectDraw::SetCooperativeLevel"), hr);
+
+        return false;
+    }
+
+    hr = pDD->SetDisplayMode(mode.w, mode.h, mode.bpp, mode.refresh, 0);
+    if ( FAILED(hr) )
+    {
+        wxLogApiError(_T("IDirectDraw::SetDisplayMode"), hr);
+
+        return false;
+    }
+
+         
+    return true;
+}
+
+bool wxDisplay::DoChangeModeWindows(const wxVideoMode& mode)
+{
+    // prepare ChangeDisplaySettingsEx() parameters
+    DEVMODE dm,
+           *pDevMode;
+    int flags;
+
+    if ( mode == wxDefaultVideoMode )
+    {
+        // reset the video mode to default
+        pDevMode = NULL;
+        flags = 0;
+    }
+    else // change to the given mode
+    {
+        wxCHECK_MSG( mode.w && mode.h, false,
+                        _T("at least the width and height must be specified") );
+
+        wxZeroMemory(dm);
+        dm.dmSize = sizeof(dm);
+        dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
+        dm.dmPelsWidth = mode.w;
+        dm.dmPelsHeight = mode.h;
+
+        if ( mode.bpp )
+        {
+            dm.dmFields |= DM_BITSPERPEL;
+            dm.dmBitsPerPel = mode.bpp;
+        }
+
+        if ( mode.refresh )
+        {
+            dm.dmFields |= DM_DISPLAYFREQUENCY;
+            dm.dmDisplayFrequency = mode.refresh;
+        }
+
+        pDevMode = &dm;
+
+        flags = CDS_FULLSCREEN;
+    }
+
+
+    // get pointer to the function dynamically
+    //
+    // we're only called from the main thread, so it's ok to use static
+    // variable
+    static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx = NULL;
+    if ( !pfnChangeDisplaySettingsEx )
+    {
+        wxDynamicLibrary dllUser32(_T("user32.dll"));
+        if ( dllUser32.IsLoaded() )
+        {
+            pfnChangeDisplaySettingsEx = (ChangeDisplaySettingsEx_t)
+                dllUser32.GetSymbol(WINFUNC(ChangeDisplaySettingsEx));
+        }
+        //else: huh, no user32.dll??
+
+        if ( !pfnChangeDisplaySettingsEx )
+        {
+            // we must be under Win95 and so there is no multiple monitors
+            // support anyhow
+            pfnChangeDisplaySettingsEx = ChangeDisplaySettingsExForWin95;
+        }
+    }
+
+    // do change the mode
+    switch ( pfnChangeDisplaySettingsEx
+             (
+                GetName(),      // display name
+                pDevMode,       // dev mode or NULL to reset
+                NULL,           // reserved
+                flags,
+                NULL            // pointer to video parameters (not used)
+             ) )
+    {
+        case DISP_CHANGE_SUCCESSFUL:
+            // ok
+            {
+                // If we have a top-level, full-screen frame, emulate
+                // the DirectX behavior and resize it.  This makes this
+                // API quite a bit easier to use.
+                wxWindow *winTop = wxTheApp->GetTopWindow();
+                wxFrame *frameTop = wxDynamicCast(winTop, wxFrame);
+                if (frameTop && frameTop->IsFullScreen())
+                {
+                    wxVideoMode current = GetCurrentMode();
+                    frameTop->SetClientSize(current.w, current.h);
+                }
+            }
+            return true;
+
+        case DISP_CHANGE_BADMODE:
+            // don't complain about this, this is the only "expected" error
+            break;
+
+        default:
+            wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") );
+    }
+
+    return false;
+}
+
+bool wxDisplay::ChangeMode(const wxVideoMode& mode)
 {
-    return wxString ( (*g_wxmswDisplayInfoArray)[m_index].m_dd.DeviceName );
+    return gs_useDirectX ? DoChangeModeDirectX(mode)
+                         : DoChangeModeWindows(mode);
 }
 
-#endif//wxUSE_DISPLAY
+#endif // wxUSE_DISPLAY
+