X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8585e2b5065cff7dfd733c8a4780dba91bde2cb2..2fb18bf45bcf391a8eb973886072dc67893e9bbe:/src/msw/display.cpp diff --git a/src/msw/display.cpp b/src/msw/display.cpp index e85dd3e787..c0e4b95986 100644 --- a/src/msw/display.cpp +++ b/src/msw/display.cpp @@ -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,10 +18,6 @@ // headers // --------------------------------------------------------------------------- -#ifdef __GNUG__ - #pragma implementation "display.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -31,7 +28,9 @@ #if wxUSE_DISPLAY #ifndef WX_PRECOMP + #include "wx/app.h" #include "wx/dynarray.h" + #include "wx/frame.h" #endif #include "wx/dynload.h" @@ -48,26 +47,40 @@ // #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 #pragma warning(disable:4706) #endif +// with mingw32, we must include windows.h first and it doesn't hurt with other +// compilers +#include + #include #ifdef _MSC_VER #pragma warning(default:4706) #endif +#include + +// 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 +89,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 +113,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 +126,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 +146,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) ) { - 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() @@ -145,31 +188,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(); - g_displays = new wxDisplayInfoArray(); + 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 + + 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 +341,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 +365,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 +378,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 +407,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 +547,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 +625,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 +652,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 +704,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 +766,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 +788,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