// 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
/////////////////////////////////////////////////////////////////////////////
// headers
// ---------------------------------------------------------------------------
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "display.h"
#endif
#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,
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
// ----------------------------------------------------------------------------
// 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;
// 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)
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)
)
{
- wxDisplayInfo* info = 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()
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)
{
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)
{
for ( size_t n = 0; n < count; n++ )
{
- if ( hmon == (*g_displays)[n].m_hmon )
+ if ( hmon == (*gs_displays)[n].m_hmon )
return n;
}
}
{
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 */
);
}
+// ----------------------------------------------------------------------------
+// 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"));
}
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;
return modes;
}
+wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const
+{
+ return gs_useDirectX ? DoGetModesDirectX(modeMatch)
+ : DoGetModesWindows(modeMatch);
+}
+
wxVideoMode wxDisplay::GetCurrentMode() const
{
wxVideoMode mode;
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,
}
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);
{
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
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