X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a536e411022b21280532c9daadc806a437fbb4c6..bf354396f61a2e1bd5544b67ecde341b6ff9bf35:/src/msw/display.cpp diff --git a/src/msw/display.cpp b/src/msw/display.cpp index cd5548c708..56845df6e8 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,7 +18,7 @@ // headers // --------------------------------------------------------------------------- -#ifdef __GNUG__ +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) #pragma implementation "display.h" #endif @@ -31,202 +32,774 @@ #if wxUSE_DISPLAY #ifndef WX_PRECOMP + #include "wx/app.h" #include "wx/dynarray.h" + #include "wx/frame.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 + #pragma warning(disable:4706) #endif -// --------------------------------------------------------------------------- -// constants -// --------------------------------------------------------------------------- +// with mingw32, we must include windows.h first and it doesn't hurt with other +// compilers +#include -// --------------------------------------------------------------------------- -// private functions -// --------------------------------------------------------------------------- +#include -void wxmswInitDisplayRectArray(); +#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 } }; // ---------------------------------------------------------------------------- -// private classes +// macros +// ---------------------------------------------------------------------------- + +#ifdef _UNICODE + #define WINFUNC(x) _T(#x) L"W" +#else + #define WINFUNC(x) #x "A" +#endif + +// ---------------------------------------------------------------------------- +// typedefs for dynamically loaded Windows functions // ---------------------------------------------------------------------------- -class wxmswDisplayInfo; +typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName, + LPDEVMODE lpDevMode, + HWND hwnd, + 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); -WX_DECLARE_OBJARRAY(wxmswDisplayInfo, wxmswDisplayInfoArray); +typedef HRESULT (WINAPI *DirectDrawCreate_t)(GUID *lpGUID, + LPDIRECTDRAW *lplpDD, + IUnknown *pUnkOuter); + +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- -class wxmswDisplayInfo +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(); } +}; + +WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray); +#include "wx/arrimpl.cpp" +WX_DEFINE_OBJARRAY(wxDisplayInfoArray); + +// 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) }; -wxmswDisplayInfoArray* g_wxmswDisplayInfoArray = 0; +IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule, wxModule) + +// ---------------------------------------------------------------------------- +// globals +// ---------------------------------------------------------------------------- + +// do we use DirectX? +static bool gs_useDirectX = false; -#include // this is a magic incantation which must be done! -WX_DEFINE_OBJARRAY(wxmswDisplayInfoArray); +// 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 ( - HMONITOR hMonitor, // handle to display monitor - HDC hdcMonitor, // handle to monitor-appropriate device context - LPRECT lprcMonitor, // pointer to monitor intersection rectangle - LPARAM dwData // data passed from EnumDisplayMonitors +// ---------------------------------------------------------------------------- +// callbacks for monitor/modes enumeration stuff +// ---------------------------------------------------------------------------- + +static BOOL CALLBACK wxmswMonitorEnumProc ( + 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) ) { - 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 WXUNUSED(driverDescription), + LPTSTR driverName, + LPVOID WXUNUSED(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; -IMPLEMENT_DYNAMIC_CLASS(wxmswDisplayModule, wxModule) + gs_displays->Add(info); + } + //else: we're called for the primary monitor, skip it + + // 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 DWORD 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 +// ---------------------------------------------------------------------------- + +// 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 - // 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); + wxDynamicLibrary dllDX(_T("ddraw.dll")); - if ( !EnumDisplayDevices ( NULL, iDevNum, &info.m_dd, 0 ) ) - wxLogLastError(wxT("EnumDisplayDevices")); +#if wxUSE_LOG + wxLog::EnableLogging(); +#endif - // get this display's Depth - DEVMODE devmode; - memset ( &devmode, 0, sizeof(devmode) ); + if ( !dllDX.IsLoaded() ) + return false; - // 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; + DirectDrawEnumerateEx_t pDDEnumEx = (DirectDrawEnumerateEx_t) + dllDX.GetSymbol(WINFUNC(DirectDrawEnumerateEx)); + if ( !pDDEnumEx ) + return false; - if ( !EnumDisplaySettings ( info.m_dd.DeviceName, ENUM_CURRENT_SETTINGS, &devmode ) ) - { - wxLogLastError(wxT("EnumDisplaySettings")); - devmode.dmFields = 0; - } + // we'll also need DirectDrawCreate() later, resolve it right now + gs_DirectDrawCreate = (DirectDrawCreate_t) + dllDX.GetSymbol(_T("DirectDrawCreate")); + if ( !gs_DirectDrawCreate ) + return false; - if ( !(devmode.dmFields&DM_BITSPERPEL) ) - info.m_depth = -1; - else - info.m_depth = devmode.dmBitsPerPel; + 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(); + + //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 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; +} + +wxString wxDisplay::GetName() const +{ + wxDisplayInfo& dpyInfo = (*gs_displays)[m_index]; + if ( dpyInfo.m_devName.empty() ) + { + 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")); + } + else + { + dpyInfo.m_devName = monInfo.szDevice; + } + } + + return dpyInfo.m_devName; } -int wxDisplay::GetDepth() const +wxString wxDisplay::GetNameForEnumSettings() const { - return (*g_wxmswDisplayInfoArray)[m_index].m_depth; + 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; } -wxString wxDisplay::GetName() 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; + + 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 wxString ( (*g_wxmswDisplayInfoArray)[m_index].m_dd.DeviceName ); + return gs_useDirectX ? DoGetModesDirectX(modeMatch) + : DoGetModesWindows(modeMatch); } -#endif//wxUSE_DISPLAY +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 gs_useDirectX ? DoChangeModeDirectX(mode) + : DoChangeModeWindows(mode); +} + +#endif // wxUSE_DISPLAY +