]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/display.cpp
Try to find comctl32.dll version even if we don't have shlwapi.h available,
[wxWidgets.git] / src / msw / display.cpp
index e85dd3e787cc9e758fb5f62813cd9642bd8262fd..56845df6e8188a49de2552dd6fffce2ae900bcc8 100644 (file)
@@ -2,10 +2,11 @@
 // Name:        display.cpp
 // Purpose:     MSW Implementation of wxDisplay class
 // Author:      Royce Mitchell III
-// Modified by:
+// Modified by: VZ (resolutions enumeration/change support, DirectDraw, ...)
+//                 Ryan Norton (IsPrimary override)
 // Created:     06/21/02
 // RCS-ID:      $Id$
-// Copyright:   (c) wxWindows team
+// Copyright:   (c) wxWidgets team
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
@@ -17,7 +18,7 @@
 // headers
 // ---------------------------------------------------------------------------
 
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
     #pragma implementation "display.h"
 #endif
 
@@ -31,7 +32,9 @@
 #if wxUSE_DISPLAY
 
 #ifndef WX_PRECOMP
+   #include "wx/app.h"
    #include "wx/dynarray.h"
+   #include "wx/frame.h"
 #endif
 
 #include "wx/dynload.h"
 //
 
 #ifdef _MSC_VER
-    // as (m)any standard header, this one doesn't compile without warnings
+    // as (m)any standard header(s), this one doesn't compile without warnings
     // with VC++ 6 <sigh>
     #pragma warning(disable:4706)
 #endif
 
+// with mingw32, we must include windows.h first and it doesn't hurt with other
+// compilers
+#include <windows.h>
+
 #include <multimon.h>
 
 #ifdef _MSC_VER
     #pragma warning(default:4706)
 #endif
 
+#include <ddraw.h>
+
+// 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 } };
+
+// ----------------------------------------------------------------------------
+// macros
+// ----------------------------------------------------------------------------
+
 #ifdef _UNICODE
-    #define MAKE_WFUNC(x) #x "W"
-    #define WINFUNC(x)  L ## MAKE_WFUNC(x)
+    #define WINFUNC(x)  _T(#x) L"W"
 #else
     #define WINFUNC(x) #x "A"
 #endif
 
 // ----------------------------------------------------------------------------
-// typedefs
+// typedefs for dynamically loaded Windows functions
 // ----------------------------------------------------------------------------
 
 typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName,
@@ -76,6 +93,20 @@ typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName,
                                                  DWORD dwFlags,
                                                  LPVOID lParam);
 
+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
 // ----------------------------------------------------------------------------
@@ -86,6 +117,12 @@ public:
     // 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;
 
@@ -93,23 +130,19 @@ public:
     // on demand by DoGetName()
     wxString m_devName;
 
-    wxDisplayInfo() { m_hmon = NULL; }
+    wxDisplayInfo() { m_hmon = NULL; m_pDD2 = NULL; }
+    ~wxDisplayInfo() { if ( m_pDD2 ) m_pDD2->Release(); }
 };
 
 WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray);
 #include "wx/arrimpl.cpp"
 WX_DEFINE_OBJARRAY(wxDisplayInfoArray);
 
-// 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 *g_displays = NULL;
-
-
-// this module is used to cleanup g_displays array
+// this module is used to cleanup gs_displays array
 class wxDisplayModule : public wxModule
 {
 public:
-    virtual bool OnInit() { return TRUE; }
+    virtual bool OnInit() { return true; }
     virtual void OnExit();
 
     DECLARE_DYNAMIC_CLASS(wxDisplayModule)
@@ -117,22 +150,36 @@ public:
 
 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
 // ===========================================================================
 
 // ----------------------------------------------------------------------------
-// local functions
+// callbacks for monitor/modes enumeration stuff
 // ----------------------------------------------------------------------------
 
 static BOOL CALLBACK wxmswMonitorEnumProc (
-  HMONITOR hMonitor,  // handle to display monitor
-  HDC hdcMonitor,     // handle to monitor-appropriate device context (NULL)
-  LPRECT lprcMonitor, // pointer to monitor intersection rectangle
-  LPARAM dwData       // data passed from EnumDisplayMonitors (unused)
+  HMONITOR hMonitor,        // handle to display monitor
+  HDC WXUNUSED(hdcMonitor), // handle to monitor-appropriate device context
+  LPRECT lprcMonitor,       // pointer to monitor intersection rectangle
+  LPARAM WXUNUSED(dwData)   // data passed from EnumDisplayMonitors (unused)
 )
 {
-    wxDisplayInfoinfo = new wxDisplayInfo();
+    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()
@@ -145,31 +192,132 @@ static BOOL CALLBACK wxmswMonitorEnumProc (
     info->m_rect.SetHeight ( lprcMonitor->bottom - lprcMonitor->top );
 
     // now add this monitor to the array
-    g_displays->Add(info);
+    gs_displays->Add(info);
 
     // continue the enumeration
-    return TRUE;
+    return true;
 }
 
-// this function must be called before accessing g_displays array as it
-// creates and initializes it
-static void InitDisplays()
+BOOL PASCAL
+wxDDEnumExCallback(GUID *pGuid,
+                   LPTSTR WXUNUSED(driverDescription),
+                   LPTSTR driverName,
+                   LPVOID WXUNUSED(lpContext),
+                   HMONITOR hmon)
 {
-    if ( g_displays )
-        return;
+    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
+
+    // continue the enumeration
+    return true;
+}
+
+HRESULT WINAPI wxDDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc,
+                                     LPVOID lpContext)
+{
+    // we need at least the mode size
+    static const DWORD FLAGS_REQUIRED = DDSD_HEIGHT | DDSD_WIDTH;
+    if ( (lpDDSurfaceDesc->dwFlags & FLAGS_REQUIRED) == FLAGS_REQUIRED )
+    {
+        wxArrayVideoModes * const modes = (wxArrayVideoModes *)lpContext;
+
+        modes->Add(wxVideoMode(lpDDSurfaceDesc->dwWidth,
+                               lpDDSurfaceDesc->dwHeight,
+                               lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount,
+                               lpDDSurfaceDesc->dwRefreshRate));
+    }
+
+    // continue the enumeration
+    return DDENUMRET_OK;
+}
+
+// ----------------------------------------------------------------------------
+// local functions
+// ----------------------------------------------------------------------------
+
+// initialize gs_displays using DirectX functions
+static bool DoInitDirectX()
+{
+#if wxUSE_LOG
+    // suppress the errors if ddraw.dll is not found
+    wxLog::EnableLogging(false);
+#endif
+
+    wxDynamicLibrary dllDX(_T("ddraw.dll"));
+
+#if wxUSE_LOG
+    wxLog::EnableLogging();
+#endif
 
-    g_displays = new wxDisplayInfoArray();
+    if ( !dllDX.IsLoaded() )
+        return false;
 
+    DirectDrawEnumerateEx_t pDDEnumEx = (DirectDrawEnumerateEx_t)
+        dllDX.GetSymbol(WINFUNC(DirectDrawEnumerateEx));
+    if ( !pDDEnumEx )
+        return false;
+
+    // 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();
+
+    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 g_displays for the
+        // TODO: still create at least one (valid) entry in gs_displays for the
         //       primary display!
     }
 }
 
+// this function must be called before accessing gs_displays array as it
+// creates and initializes it
+static void InitDisplays()
+{
+    if ( gs_displays )
+        return;
+
+    gs_displays = new wxDisplayInfoArray();
+
+    if ( !gs_useDirectX || !DoInitDirectX() )
+    {
+        // either we were told not to try to use DirectX or fall back to std
+        // functions if DirectX method failed
+        gs_useDirectX = false;
+
+        DoInitStdWindows();
+    }
+}
+
 // convert a DEVMODE to our wxVideoMode
 wxVideoMode ConvertToVideoMode(const DEVMODE& dm)
 {
@@ -197,13 +345,21 @@ LONG WINAPI ChangeDisplaySettingsExForWin95(LPCTSTR WXUNUSED(lpszDeviceName),
 
 void wxDisplayModule::OnExit()
 {
-    delete g_displays;
+    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)
 {
@@ -213,7 +369,7 @@ static int DisplayFromHMONITOR(HMONITOR hmon)
 
         for ( size_t n = 0; n < count; n++ )
         {
-            if ( hmon == (*g_displays)[n].m_hmon )
+            if ( hmon == (*gs_displays)[n].m_hmon )
                 return n;
         }
     }
@@ -226,12 +382,14 @@ size_t wxDisplayBase::GetCount()
 {
     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( g_displays->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS),
-                    _T("So how many displays does this system have?") );
+    //RN: FIXME:  This is wrong - the display info array should reload after every call
+    //to GetCount() - otherwise it will not be accurate.
+    //The user can change the number of displays in display properties/settings
+    //after GetCount or similar is called and really mess this up...  
+    //wxASSERT_MSG( gs_displays->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS),
+    //                _T("So how many displays does this system have?") );
 
-    return g_displays->GetCount();
+    return gs_displays->GetCount();
 }
 
 /* static */
@@ -253,29 +411,118 @@ int wxDisplayBase::GetFromWindow(wxWindow *window)
            );
 }
 
+// ----------------------------------------------------------------------------
+// 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();
+
+    if ( gs_useDirectX )
+    {
+        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();
+    }
+}
+
+wxDisplay::~wxDisplay()
+{
+    wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
+
+    LPDIRECTDRAW2& pDD2 = dpyInfo.m_pDD2;
+    if ( pDD2 )
+    {
+        pDD2->Release();
+    }
+}
+
+// ----------------------------------------------------------------------------
+// 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_displays)[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;
 }
 
 wxString wxDisplay::GetName() const
 {
-    wxDisplayInfo& dpyInfo = (*g_displays)[m_index];
+    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) )
+        // NB: Cast from MONITORINFOEX* to MONITORINFO* is done because
+        //     Mingw headers - unlike the ones from Microsoft's Platform SDK -
+        //     don't derive the former from the latter in C++ mode and so
+        //     the pointer's type is not converted implicitly.
+        if ( !::GetMonitorInfo(dpyInfo.m_hmon, (LPMONITORINFO)&monInfo) )
         {
             wxLogLastError(_T("GetMonitorInfo"));
         }
@@ -304,7 +551,62 @@ wxString wxDisplay::GetNameForEnumSettings() const
     return name;
 }
 
-wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const
+// ----------------------------------------------------------------------------
+// determine if this is the primary display
+// ----------------------------------------------------------------------------
+
+bool wxDisplay::IsPrimary() const
+{
+    wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
+
+    MONITORINFOEX monInfo;
+    wxZeroMemory(monInfo);
+    monInfo.cbSize = sizeof(monInfo);
+
+    // NB: Cast from MONITORINFOEX* to MONITORINFO* is done because
+    //     Mingw headers - unlike the ones from Microsoft's Platform SDK -
+    //     don't derive the former from the latter in C++ mode and so
+    //     the pointer's type is not converted implicitly.
+    if ( !::GetMonitorInfo(dpyInfo.m_hmon, (LPMONITORINFO)&monInfo) )
+    {
+        wxLogLastError(_T("GetMonitorInfo"));
+    }
+
+    return (monInfo.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY;
+}
+
+// ----------------------------------------------------------------------------
+// video modes enumeration
+// ----------------------------------------------------------------------------
+
+wxArrayVideoModes
+wxDisplay::DoGetModesDirectX(const wxVideoMode& WXUNUSED(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;
 
@@ -327,6 +629,12 @@ wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const
     return modes;
 }
 
+wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const
+{
+    return gs_useDirectX ? DoGetModesDirectX(modeMatch)
+                         : DoGetModesWindows(modeMatch);
+}
+
 wxVideoMode wxDisplay::GetCurrentMode() const
 {
     wxVideoMode mode;
@@ -348,7 +656,44 @@ wxVideoMode wxDisplay::GetCurrentMode() const
     return mode;
 }
 
-bool wxDisplay::ChangeMode(const wxVideoMode& 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,
@@ -363,7 +708,7 @@ bool wxDisplay::ChangeMode(const wxVideoMode& mode)
     }
     else // change to the given mode
     {
-        wxCHECK_MSG( mode.w && mode.h, FALSE,
+        wxCHECK_MSG( mode.w && mode.h, false,
                         _T("at least the width and height must be specified") );
 
         wxZeroMemory(dm);
@@ -425,7 +770,19 @@ bool wxDisplay::ChangeMode(const wxVideoMode& mode)
     {
         case DISP_CHANGE_SUCCESSFUL:
             // ok
-            return TRUE;
+            {
+                // 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
@@ -435,7 +792,13 @@ bool wxDisplay::ChangeMode(const wxVideoMode& mode)
             wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") );
     }
 
-    return FALSE;
+    return false;
+}
+
+bool wxDisplay::ChangeMode(const wxVideoMode& mode)
+{
+    return gs_useDirectX ? DoChangeModeDirectX(mode)
+                         : DoChangeModeWindows(mode);
 }
 
 #endif // wxUSE_DISPLAY