1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     MSW Implementation of wxDisplay class 
   4 // Author:      Royce Mitchell III 
   5 // Modified by: VZ (resolutions enumeration/change support, DirectDraw, ...) 
   8 // Copyright:   (c) wxWindows team 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // =========================================================================== 
  14 // =========================================================================== 
  16 // --------------------------------------------------------------------------- 
  18 // --------------------------------------------------------------------------- 
  20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "display.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  34    #include "wx/dynarray.h" 
  37 #include "wx/dynload.h" 
  39 #include "wx/display.h" 
  41 // the following define is necessary to access the multi-monitor function 
  42 // declarations in a manner safe to use w/ Windows 95 
  43 #define COMPILE_MULTIMON_STUBS 
  45 // if you don't have multimon.h you can download the file from: 
  47 //  http://www.microsoft.com/msj/0697/monitor/monitortextfigs.htm#fig4 
  51     // as (m)any standard header(s), this one doesn't compile without warnings 
  53     #pragma warning(disable:4706) 
  59     #pragma warning(default:4706) 
  64 // we don't want to link with ddraw.lib which contains the real 
  65 // IID_IDirectDraw2 definition 
  66 const GUID wxIID_IDirectDraw2 
= 
  67     { 0xB3A6F3E0, 0x2B43, 0x11CF, { 0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 } }; 
  69 // ---------------------------------------------------------------------------- 
  71 // ---------------------------------------------------------------------------- 
  74     #define WINFUNC(x)  _T(#x) L"W" 
  76     #define WINFUNC(x) #x "A" 
  79 // ---------------------------------------------------------------------------- 
  80 // typedefs for dynamically loaded Windows functions 
  81 // ---------------------------------------------------------------------------- 
  83 typedef LONG (WINAPI 
*ChangeDisplaySettingsEx_t
)(LPCTSTR lpszDeviceName
, 
  89 typedef BOOL (PASCAL 
*DDEnumExCallback_t
)(GUID 
*pGuid
, 
  90                                           LPTSTR driverDescription
, 
  95 typedef HRESULT (WINAPI 
*DirectDrawEnumerateEx_t
)(DDEnumExCallback_t lpCallback
, 
  99 typedef HRESULT (WINAPI 
*DirectDrawCreate_t
)(GUID 
*lpGUID
, 
 100                                              LPDIRECTDRAW 
*lplpDD
, 
 101                                              IUnknown 
*pUnkOuter
); 
 103 // ---------------------------------------------------------------------------- 
 105 // ---------------------------------------------------------------------------- 
 110     // handle of this monitor used by MonitorXXX() functions, never NULL 
 113     // IDirectDraw object used to control this display, may be NULL 
 114     IDirectDraw2 
*m_pDD2
; 
 116     // DirectDraw GUID for this display, only valid when using DirectDraw 
 119     // the entire area of this monitor in virtual screen coordinates 
 122     // the display device name for this monitor, empty initially and retrieved 
 123     // on demand by DoGetName() 
 126     wxDisplayInfo() { m_hmon 
= NULL
; m_pDD2 
= NULL
; } 
 127     ~wxDisplayInfo() { if ( m_pDD2 
) m_pDD2
->Release(); } 
 130 WX_DECLARE_OBJARRAY(wxDisplayInfo
, wxDisplayInfoArray
); 
 131 #include "wx/arrimpl.cpp" 
 132 WX_DEFINE_OBJARRAY(wxDisplayInfoArray
); 
 134 // this module is used to cleanup gs_displays array 
 135 class wxDisplayModule 
: public wxModule
 
 138     virtual bool OnInit() { return true; } 
 139     virtual void OnExit(); 
 141     DECLARE_DYNAMIC_CLASS(wxDisplayModule
) 
 144 IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule
, wxModule
) 
 146 // ---------------------------------------------------------------------------- 
 148 // ---------------------------------------------------------------------------- 
 150 // do we use DirectX? 
 151 static bool gs_useDirectX 
= false; 
 153 // dynamically resolved DirectDrawCreate() 
 154 static DirectDrawCreate_t gs_DirectDrawCreate 
= NULL
; 
 156 // this is not really MT-unsafe as wxDisplay is only going to be used from the 
 157 // main thread, i.e. we consider that it's a GUI class and so don't protect it 
 158 static wxDisplayInfoArray 
*gs_displays 
= NULL
; 
 160 // =========================================================================== 
 162 // =========================================================================== 
 164 // ---------------------------------------------------------------------------- 
 165 // callbacks for monitor/modes enumeration stuff 
 166 // ---------------------------------------------------------------------------- 
 168 static BOOL CALLBACK 
wxmswMonitorEnumProc ( 
 169   HMONITOR hMonitor
,        // handle to display monitor 
 170   HDC 
WXUNUSED(hdcMonitor
), // handle to monitor-appropriate device context 
 171   LPRECT lprcMonitor
,       // pointer to monitor intersection rectangle 
 172   LPARAM 
WXUNUSED(dwData
)   // data passed from EnumDisplayMonitors (unused) 
 175     wxDisplayInfo 
*info 
= new wxDisplayInfo(); 
 177     // we need hMonitor to be able to map display id to it which is needed for 
 178     // MonitorXXX() functions, in particular MonitorFromPoint() 
 179     info
->m_hmon 
= hMonitor
; 
 181     // we also store the display geometry 
 182     info
->m_rect
.SetX ( lprcMonitor
->left 
); 
 183     info
->m_rect
.SetY ( lprcMonitor
->top 
); 
 184     info
->m_rect
.SetWidth ( lprcMonitor
->right 
- lprcMonitor
->left 
); 
 185     info
->m_rect
.SetHeight ( lprcMonitor
->bottom 
- lprcMonitor
->top 
); 
 187     // now add this monitor to the array 
 188     gs_displays
->Add(info
); 
 190     // continue the enumeration 
 195 wxDDEnumExCallback(GUID 
*pGuid
, 
 196                    LPTSTR 
WXUNUSED(driverDescription
), 
 198                    LPVOID 
WXUNUSED(lpContext
), 
 203         wxDisplayInfo 
*info 
= new wxDisplayInfo(); 
 206         info
->m_guid 
= *pGuid
; 
 207         info
->m_devName 
= driverName
; 
 209         gs_displays
->Add(info
); 
 211     //else: we're called for the primary monitor, skip it 
 213     // continue the enumeration 
 217 HRESULT WINAPI 
wxDDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc
,   
 220     // we need at least the mode size 
 221     static const int FLAGS_REQUIRED 
= DDSD_HEIGHT 
| DDSD_WIDTH
; 
 222     if ( (lpDDSurfaceDesc
->dwFlags 
& FLAGS_REQUIRED
) == FLAGS_REQUIRED 
) 
 224         wxArrayVideoModes 
* const modes 
= (wxArrayVideoModes 
*)lpContext
; 
 226         modes
->Add(wxVideoMode(lpDDSurfaceDesc
->dwWidth
, 
 227                                lpDDSurfaceDesc
->dwHeight
, 
 228                                lpDDSurfaceDesc
->ddpfPixelFormat
.dwRGBBitCount
, 
 229                                lpDDSurfaceDesc
->dwRefreshRate
)); 
 232     // continue the enumeration 
 236 // ---------------------------------------------------------------------------- 
 238 // ---------------------------------------------------------------------------- 
 240 // initialize gs_displays using DirectX functions 
 241 static bool DoInitDirectX() 
 243     // suppress the errors if ddraw.dll is not found 
 244     wxLog::EnableLogging(false); 
 246     wxDynamicLibrary 
dllDX(_T("ddraw.dll")); 
 248     wxLog::EnableLogging(true); 
 250     if ( !dllDX
.IsLoaded() ) 
 253     DirectDrawEnumerateEx_t pDDEnumEx 
= (DirectDrawEnumerateEx_t
) 
 254         dllDX
.GetSymbol(WINFUNC(DirectDrawEnumerateEx
)); 
 258     // we'll also need DirectDrawCreate() later, resolve it right now 
 259     gs_DirectDrawCreate 
= (DirectDrawCreate_t
) 
 260         dllDX
.GetSymbol(_T("DirectDrawCreate")); 
 261     if ( !gs_DirectDrawCreate 
) 
 264     if ( (*pDDEnumEx
)(wxDDEnumExCallback
, 
 266                       DDENUM_ATTACHEDSECONDARYDEVICES
) != DD_OK 
) 
 271     // ok, it seems like we're going to use DirectDraw and so we're going to 
 272     // need ddraw.dll all the time, don't unload it 
 278 // initialize gs_displays using the standard Windows functions 
 279 static void DoInitStdWindows() 
 281     // enumerate all displays 
 282     if ( !::EnumDisplayMonitors(NULL
, NULL
, wxmswMonitorEnumProc
, 0) ) 
 284         wxLogLastError(wxT("EnumDisplayMonitors")); 
 286         // TODO: still create at least one (valid) entry in gs_displays for the 
 291 // this function must be called before accessing gs_displays array as it 
 292 // creates and initializes it 
 293 static void InitDisplays() 
 298     gs_displays 
= new wxDisplayInfoArray(); 
 300     if ( !gs_useDirectX 
|| !DoInitDirectX() ) 
 302         // either we were told not to try to use DirectX or fall back to std 
 303         // functions if DirectX method failed 
 304         gs_useDirectX 
= false; 
 310 // convert a DEVMODE to our wxVideoMode 
 311 wxVideoMode 
ConvertToVideoMode(const DEVMODE
& dm
) 
 313     // note that dmDisplayFrequency may be 0 or 1 meaning "standard one" and 
 314     // although 0 is ok for us we don't want to return modes with 1hz refresh 
 315     return wxVideoMode(dm
.dmPelsWidth
, 
 318                        dm
.dmDisplayFrequency 
> 1 ? dm
.dmDisplayFrequency 
: 0); 
 321 // emulation of ChangeDisplaySettingsEx() for Win95 
 322 LONG WINAPI 
ChangeDisplaySettingsExForWin95(LPCTSTR 
WXUNUSED(lpszDeviceName
), 
 326                                             LPVOID 
WXUNUSED(lParam
)) 
 328     return ::ChangeDisplaySettings(lpDevMode
, dwFlags
); 
 331 // ---------------------------------------------------------------------------- 
 333 // ---------------------------------------------------------------------------- 
 335 void wxDisplayModule::OnExit() 
 340 // --------------------------------------------------------------------------- 
 342 // --------------------------------------------------------------------------- 
 345 void wxDisplay::UseDirectX(bool useDX
) 
 347     wxCHECK_RET( !gs_displays
, _T("it is too late to call UseDirectX") ); 
 349     gs_useDirectX 
= useDX
; 
 352 // helper of GetFromPoint() and GetFromWindow() 
 353 static int DisplayFromHMONITOR(HMONITOR hmon
) 
 357         const size_t count 
= wxDisplay::GetCount(); 
 359         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 361             if ( hmon 
== (*gs_displays
)[n
].m_hmon 
) 
 370 size_t wxDisplayBase::GetCount() 
 374     // I'm not sure if they really always return the same thing and if this is 
 375     // not true I'd like to know in which situation does it happen 
 376     wxASSERT_MSG( gs_displays
->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS
), 
 377                     _T("So how many displays does this system have?") ); 
 379     return gs_displays
->GetCount(); 
 383 int wxDisplayBase::GetFromPoint ( const wxPoint
& pt 
) 
 389     return DisplayFromHMONITOR(::MonitorFromPoint(pt2
, MONITOR_DEFAULTTONULL
)); 
 393 int wxDisplayBase::GetFromWindow(wxWindow 
*window
) 
 395     return DisplayFromHMONITOR
 
 397             ::MonitorFromWindow(GetHwndOf(window
), MONITOR_DEFAULTTONULL
) 
 401 // ---------------------------------------------------------------------------- 
 402 // wxDisplay ctor/dtor 
 403 // ---------------------------------------------------------------------------- 
 405 wxDisplay::wxDisplay ( size_t n 
) 
 406          : wxDisplayBase ( n 
) 
 408     // if we do this in ctor we won't have to call it from all the member 
 414         wxDisplayInfo
& dpyInfo 
= (*gs_displays
)[n
]; 
 416         LPDIRECTDRAW2
& pDD2 
= dpyInfo
.m_pDD2
; 
 419             if ( !gs_DirectDrawCreate 
) 
 426             HRESULT hr 
= (*gs_DirectDrawCreate
)(&dpyInfo
.m_guid
, &pDD
, NULL
); 
 428             if ( FAILED(hr
) || !pDD 
) 
 431                 wxLogApiError(_T("DirectDrawCreate"), hr
); 
 433             else // got IDirectDraw, we want IDirectDraw2 
 435                 hr 
= pDD
->QueryInterface(wxIID_IDirectDraw2
, (void **)&pDD2
); 
 436                 if ( FAILED(hr
) || !pDD2 
) 
 438                     wxLogApiError(_T("IDirectDraw::QueryInterface(IDD2)"), hr
); 
 444         //else: DirectDraw object corresponding to our display already exists 
 446         // increment its ref count to account for Release() in dtor 
 448         // NB: pDD2 will be only really Release()d when gs_displays is 
 449         //     destroyed which is ok as we don't want to recreate DD objects 
 455 wxDisplay::~wxDisplay() 
 457     wxDisplayInfo
& dpyInfo 
= (*gs_displays
)[m_index
]; 
 459     LPDIRECTDRAW2
& pDD2 
= dpyInfo
.m_pDD2
; 
 466 // ---------------------------------------------------------------------------- 
 467 // wxDisplay simple accessors 
 468 // ---------------------------------------------------------------------------- 
 470 bool wxDisplay::IsOk() const 
 472     return m_index 
< GetCount() && 
 473                 (!gs_useDirectX 
|| (*gs_displays
)[m_index
].m_pDD2
); 
 476 wxRect 
wxDisplay::GetGeometry() const 
 478     wxDisplayInfo
& dpyInfo 
= (*gs_displays
)[m_index
]; 
 479     wxRect
& rect 
= dpyInfo
.m_rect
; 
 483         wxZeroMemory(monInfo
); 
 484         monInfo
.cbSize 
= sizeof(monInfo
); 
 486         if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, &monInfo
) ) 
 488             wxLogLastError(_T("GetMonitorInfo")); 
 492             wxCopyRECTToRect(monInfo
.rcMonitor
, rect
); 
 499 wxString 
wxDisplay::GetName() const 
 501     wxDisplayInfo
& dpyInfo 
= (*gs_displays
)[m_index
]; 
 502     if ( dpyInfo
.m_devName
.empty() ) 
 504         MONITORINFOEX monInfo
; 
 505         wxZeroMemory(monInfo
); 
 506         monInfo
.cbSize 
= sizeof(monInfo
); 
 508         if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, &monInfo
) ) 
 510             wxLogLastError(_T("GetMonitorInfo")); 
 514             dpyInfo
.m_devName 
= monInfo
.szDevice
; 
 518     return dpyInfo
.m_devName
; 
 521 wxString 
wxDisplay::GetNameForEnumSettings() const 
 524     const bool isWin95 
= wxGetOsVersion(&major
, &minor
) == wxWIN95 
&& 
 525                             major 
== 4 && minor 
== 0; 
 527     // the first parameter of EnumDisplaySettings() must be NULL under Win95 
 528     // according to MSDN but GetMonitorInfo() stub in multimon.h still returns 
 529     // something even in this case, so we have to correct this manually 
 537 // ---------------------------------------------------------------------------- 
 538 // video modes enumeration 
 539 // ---------------------------------------------------------------------------- 
 542 wxDisplay::DoGetModesDirectX(const wxVideoMode
& WXUNUSED(modeMatch
)) const 
 544     wxArrayVideoModes modes
; 
 546     IDirectDraw2 
*pDD 
= (*gs_displays
)[m_index
].m_pDD2
; 
 550         HRESULT hr 
= pDD
->EnumDisplayModes
 
 553                             NULL
,   // all modes (TODO: use modeMatch!) 
 554                             &modes
,   // callback parameter 
 555                             wxDDEnumModesCallback
 
 560             wxLogApiError(_T("IDirectDraw::EnumDisplayModes"), hr
); 
 568 wxDisplay::DoGetModesWindows(const wxVideoMode
& modeMatch
) const 
 570     wxArrayVideoModes modes
; 
 572     const wxString name 
= GetNameForEnumSettings(); 
 574     const wxChar 
* const deviceName 
= name
.empty() ? NULL 
: name
.c_str(); 
 577     for ( int iModeNum 
= 0; 
 578           ::EnumDisplaySettings(deviceName
, iModeNum
, &dm
); 
 581         const wxVideoMode mode 
= ConvertToVideoMode(dm
); 
 582         if ( mode
.Matches(modeMatch
) ) 
 591 wxArrayVideoModes 
wxDisplay::GetModes(const wxVideoMode
& modeMatch
) const 
 593     return gs_useDirectX 
? DoGetModesDirectX(modeMatch
) 
 594                          : DoGetModesWindows(modeMatch
); 
 597 wxVideoMode 
wxDisplay::GetCurrentMode() const 
 601     const wxString name 
= GetNameForEnumSettings(); 
 604     if ( !::EnumDisplaySettings(name
.empty() ? NULL 
: name
.c_str(), 
 605                                 ENUM_CURRENT_SETTINGS
, 
 608         wxLogLastError(_T("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)")); 
 612         mode 
= ConvertToVideoMode(dm
); 
 618 // ---------------------------------------------------------------------------- 
 619 // video mode switching 
 620 // ---------------------------------------------------------------------------- 
 622 bool wxDisplay::DoChangeModeDirectX(const wxVideoMode
& mode
) 
 624     IDirectDraw2 
*pDD 
= (*gs_displays
)[m_index
].m_pDD2
; 
 628     wxWindow 
*winTop 
= wxTheApp
->GetTopWindow(); 
 629     wxCHECK_MSG( winTop
, false, _T("top level window required for DirectX") ); 
 631     HRESULT hr 
= pDD
->SetCooperativeLevel
 
 634                         DDSCL_EXCLUSIVE 
| DDSCL_FULLSCREEN
 
 638         wxLogApiError(_T("IDirectDraw::SetCooperativeLevel"), hr
); 
 643     hr 
= pDD
->SetDisplayMode(mode
.w
, mode
.h
, mode
.bpp
, mode
.refresh
, 0); 
 646         wxLogApiError(_T("IDirectDraw::SetDisplayMode"), hr
); 
 655 bool wxDisplay::DoChangeModeWindows(const wxVideoMode
& mode
) 
 657     // prepare ChangeDisplaySettingsEx() parameters 
 662     if ( mode 
== wxDefaultVideoMode 
) 
 664         // reset the video mode to default 
 668     else // change to the given mode 
 670         wxCHECK_MSG( mode
.w 
&& mode
.h
, false, 
 671                         _T("at least the width and height must be specified") ); 
 674         dm
.dmSize 
= sizeof(dm
); 
 675         dm
.dmFields 
= DM_PELSWIDTH 
| DM_PELSHEIGHT
; 
 676         dm
.dmPelsWidth 
= mode
.w
; 
 677         dm
.dmPelsHeight 
= mode
.h
; 
 681             dm
.dmFields 
|= DM_BITSPERPEL
; 
 682             dm
.dmBitsPerPel 
= mode
.bpp
; 
 687             dm
.dmFields 
|= DM_DISPLAYFREQUENCY
; 
 688             dm
.dmDisplayFrequency 
= mode
.refresh
; 
 693         flags 
= CDS_FULLSCREEN
; 
 697     // get pointer to the function dynamically 
 699     // we're only called from the main thread, so it's ok to use static 
 701     static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx 
= NULL
; 
 702     if ( !pfnChangeDisplaySettingsEx 
) 
 704         wxDynamicLibrary 
dllUser32(_T("user32.dll")); 
 705         if ( dllUser32
.IsLoaded() ) 
 707             pfnChangeDisplaySettingsEx 
= (ChangeDisplaySettingsEx_t
) 
 708                 dllUser32
.GetSymbol(WINFUNC(ChangeDisplaySettingsEx
)); 
 710         //else: huh, no user32.dll?? 
 712         if ( !pfnChangeDisplaySettingsEx 
) 
 714             // we must be under Win95 and so there is no multiple monitors 
 716             pfnChangeDisplaySettingsEx 
= ChangeDisplaySettingsExForWin95
; 
 720     // do change the mode 
 721     switch ( pfnChangeDisplaySettingsEx
 
 723                 GetName(),      // display name 
 724                 pDevMode
,       // dev mode or NULL to reset 
 727                 NULL            
// pointer to video parameters (not used) 
 730         case DISP_CHANGE_SUCCESSFUL
: 
 733                 // If we have a top-level, full-screen frame, emulate 
 734                 // the DirectX behavior and resize it.  This makes this 
 735                 // API quite a bit easier to use. 
 736                 wxWindow 
*winTop 
= wxTheApp
->GetTopWindow(); 
 737                 wxFrame 
*frameTop 
= wxDynamicCast(winTop
, wxFrame
); 
 738                 if (frameTop 
&& frameTop
->IsFullScreen()) 
 740                     wxVideoMode current 
= GetCurrentMode(); 
 741                     frameTop
->SetClientSize(current
.w
, current
.h
); 
 746         case DISP_CHANGE_BADMODE
: 
 747             // don't complain about this, this is the only "expected" error 
 751             wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") ); 
 757 bool wxDisplay::ChangeMode(const wxVideoMode
& mode
) 
 759     return gs_useDirectX 
? DoChangeModeDirectX(mode
) 
 760                          : DoChangeModeWindows(mode
); 
 763 #endif // wxUSE_DISPLAY