X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a536e411022b21280532c9daadc806a437fbb4c6..abd474ea63667f727940a009cc3e0b23ba9f418f:/src/msw/display.cpp diff --git a/src/msw/display.cpp b/src/msw/display.cpp index cd5548c708..b7abb30192 100644 --- a/src/msw/display.cpp +++ b/src/msw/display.cpp @@ -1,11 +1,12 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: display.cpp +// Name: src/msw/display.cpp // Purpose: MSW Implementation of wxDisplay class -// Author: Royce Mitchell III -// Modified by: +// Author: Royce Mitchell III, Vadim Zeitlin +// Modified by: Ryan Norton (IsPrimary override) // Created: 06/21/02 // RCS-ID: $Id$ -// Copyright: (c) wxWindows team +// Copyright: (c) wxWidgets team +// Copyright: (c) 2002-2006 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" @@ -30,203 +27,505 @@ #if wxUSE_DISPLAY +#include "wx/display.h" + #ifndef WX_PRECOMP - #include "wx/dynarray.h" + #include "wx/dynarray.h" + #include "wx/app.h" + #include "wx/frame.h" #endif -#include "wx/display.h" +#include "wx/dynload.h" +#include "wx/sysopt.h" + +#include "wx/display_impl.h" +#include "wx/msw/wrapwin.h" +#include "wx/msw/missing.h" +#include "wx/msw/private.h" + +#ifndef __WXWINCE__ + // Older versions of windef.h don't define HMONITOR. Unfortunately, we + // can't directly test whether HMONITOR is defined or not in windef.h as + // it's not a macro but a typedef, so we test for an unrelated symbol which + // is only defined in winuser.h if WINVER >= 0x0500 + #if !defined(HMONITOR_DECLARED) && !defined(MNS_NOCHECK) + DECLARE_HANDLE(HMONITOR); + typedef BOOL(CALLBACK * MONITORENUMPROC )(HMONITOR, HDC, LPRECT, LPARAM); + typedef struct tagMONITORINFO + { + DWORD cbSize; + RECT rcMonitor; + RECT rcWork; + DWORD dwFlags; + } MONITORINFO, *LPMONITORINFO; + typedef struct tagMONITORINFOEX : public tagMONITORINFO + { + TCHAR szDevice[CCHDEVICENAME]; + } MONITORINFOEX, *LPMONITORINFOEX; + #define MONITOR_DEFAULTTONULL 0x00000000 + #define MONITOR_DEFAULTTOPRIMARY 0x00000001 + #define MONITOR_DEFAULTTONEAREST 0x00000002 + #define MONITORINFOF_PRIMARY 0x00000001 + #define HMONITOR_DECLARED + #endif +#endif // !__WXWINCE__ + +// display functions are found in different DLLs under WinCE and normal Win32 +#ifdef __WXWINCE__ +static const wxChar displayDllName[] = wxT("coredll.dll"); +#else +static const wxChar displayDllName[] = wxT("user32.dll"); +#endif -// 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 +// ---------------------------------------------------------------------------- +// typedefs for dynamically loaded Windows functions +// ---------------------------------------------------------------------------- -#if 0 -#define COMPILE_MULTIMON_STUBS -#include "wx/msw/multimon.h" -#endif +typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName, + LPDEVMODE lpDevMode, + HWND hwnd, + DWORD dwFlags, + LPVOID lParam); + +typedef BOOL (WINAPI *EnumDisplayMonitors_t)(HDC,LPCRECT,MONITORENUMPROC,LPARAM); +typedef HMONITOR (WINAPI *MonitorFromPoint_t)(POINT,DWORD); +typedef HMONITOR (WINAPI *MonitorFromWindow_t)(HWND,DWORD); +typedef BOOL (WINAPI *GetMonitorInfo_t)(HMONITOR,LPMONITORINFO); + +#ifndef __WXWINCE__ +// 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); +} +#endif // !__WXWINCE__ -// --------------------------------------------------------------------------- -// constants -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// private functions -// --------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// wxDisplayMSW declaration +// ---------------------------------------------------------------------------- + +class wxDisplayMSW : public wxDisplayImpl +{ +public: + wxDisplayMSW(unsigned n, HMONITOR hmon) + : wxDisplayImpl(n), + m_hmon(hmon) + { + } + + virtual wxRect GetGeometry() const; + virtual wxRect GetClientArea() const; + virtual wxString GetName() const; + virtual bool IsPrimary() const; + + virtual wxVideoMode GetCurrentMode() const; + virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const; + virtual bool ChangeMode(const wxVideoMode& mode); + +protected: + // convert a DEVMODE to our wxVideoMode + static 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); + } + + // Call GetMonitorInfo() and fill in the provided struct and return true if + // it succeeded, otherwise return false. + bool GetMonInfo(MONITORINFOEX& monInfo) const; + + HMONITOR m_hmon; + +private: + wxDECLARE_NO_COPY_CLASS(wxDisplayMSW); +}; -void wxmswInitDisplayRectArray(); // ---------------------------------------------------------------------------- -// private classes +// wxDisplayFactoryMSW declaration // ---------------------------------------------------------------------------- -class wxmswDisplayInfo; +WX_DEFINE_ARRAY(HMONITOR, wxMonitorHandleArray); -WX_DECLARE_OBJARRAY(wxmswDisplayInfo, wxmswDisplayInfoArray); +// functions dynamically bound by wxDisplayFactoryMSW ctor. +static MonitorFromPoint_t gs_MonitorFromPoint = NULL; +static MonitorFromWindow_t gs_MonitorFromWindow = NULL; +static GetMonitorInfo_t gs_GetMonitorInfo = NULL; +static EnumDisplayMonitors_t gs_EnumDisplayMonitors = NULL; -class wxmswDisplayInfo +class wxDisplayFactoryMSW : public wxDisplayFactory { public: - HMONITOR m_hmon; - DISPLAY_DEVICE m_dd; - wxRect m_rect; - int m_depth; + // ctor checks if the current system supports multimon API and dynamically + // bind the functions we need if this is the case and fills the + // m_displays array if they're available + wxDisplayFactoryMSW(); + + bool IsOk() const { return !m_displays.empty(); } + + virtual wxDisplayImpl *CreateDisplay(unsigned n); + virtual unsigned GetCount() { return unsigned(m_displays.size()); } + virtual int GetFromPoint(const wxPoint& pt); + virtual int GetFromWindow(const wxWindow *window); + +private: + // EnumDisplayMonitors() callback + static BOOL CALLBACK MultimonEnumProc(HMONITOR hMonitor, + HDC hdcMonitor, + LPRECT lprcMonitor, + LPARAM dwData); + + // find the monitor corresponding to the given handle, + // return wxNOT_FOUND if not found + int FindDisplayFromHMONITOR(HMONITOR hmon) const; + + // the array containing information about all available displays, filled by + // MultimonEnumProc() + wxMonitorHandleArray m_displays; + + wxDECLARE_NO_COPY_CLASS(wxDisplayFactoryMSW); }; -wxmswDisplayInfoArray* g_wxmswDisplayInfoArray = 0; -#include // this is a magic incantation which must be done! -WX_DEFINE_OBJARRAY(wxmswDisplayInfoArray); +// ---------------------------------------------------------------------------- +// wxDisplay implementation +// ---------------------------------------------------------------------------- -// =========================================================================== -// implementation -// =========================================================================== +/* static */ wxDisplayFactory *wxDisplay::CreateFactory() +{ + wxDisplayFactoryMSW *factoryMM = new wxDisplayFactoryMSW; + + if ( factoryMM->IsOk() ) + return factoryMM; + + delete factoryMM; + + // fall back to a stub implementation if no multimon support (Win95?) + return new wxDisplayFactorySingle; +} + + +// ---------------------------------------------------------------------------- +// wxDisplayMSW implementation +// ---------------------------------------------------------------------------- + +bool wxDisplayMSW::GetMonInfo(MONITORINFOEX& monInfo) const +{ + if ( !gs_GetMonitorInfo(m_hmon, &monInfo) ) + { + wxLogLastError(wxT("GetMonitorInfo")); + return false; + } + + return true; +} -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 -) +wxRect wxDisplayMSW::GetGeometry() const { - wxmswDisplayInfo* info = new wxmswDisplayInfo(); - info->m_hmon = hMonitor; - 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 ); + WinStruct monInfo; - return TRUE; // continue the enumeration + wxRect rect; + if ( GetMonInfo(monInfo) ) + wxCopyRECTToRect(monInfo.rcMonitor, rect); + + return rect; } -class wxmswDisplayModule : public wxModule +wxRect wxDisplayMSW::GetClientArea() const { - DECLARE_DYNAMIC_CLASS(wxmswDisplayModule) -public: - wxmswDisplayModule() {} - bool OnInit(); - void OnExit(); -}; + WinStruct monInfo; + + wxRect rectClient; + if ( GetMonInfo(monInfo) ) + wxCopyRECTToRect(monInfo.rcWork, rectClient); + + return rectClient; +} + +wxString wxDisplayMSW::GetName() const +{ + WinStruct monInfo; + + wxString name; + if ( GetMonInfo(monInfo) ) + name = monInfo.szDevice; + + return name; +} + +bool wxDisplayMSW::IsPrimary() const +{ + WinStruct monInfo; + + if ( !GetMonInfo(monInfo) ) + return false; -IMPLEMENT_DYNAMIC_CLASS(wxmswDisplayModule, wxModule) + return (monInfo.dwFlags & MONITORINFOF_PRIMARY) != 0; +} -bool wxmswDisplayModule::OnInit() +wxVideoMode wxDisplayMSW::GetCurrentMode() const { - g_wxmswDisplayInfoArray = new wxmswDisplayInfoArray(); - if ( !g_wxmswDisplayInfoArray ) + wxVideoMode mode; + + // The first parameter of EnumDisplaySettings() must be NULL under Win95 + // according to MSDN. The version of GetName() we implement for Win95 + // returns an empty string. + const wxString name = GetName(); + const wxChar * const deviceName = name.empty() + ? (const wxChar*)NULL + : (const wxChar*)name.c_str(); + + DEVMODE dm; + dm.dmSize = sizeof(dm); + dm.dmDriverExtra = 0; + + if ( !::EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, &dm) ) + { + wxLogLastError(wxT("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)")); + } + else { - wxFAIL_MSG(wxT("Couldn't allocate array for display information")); - return FALSE; + mode = ConvertToVideoMode(dm); } - // 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")); + return mode; +} - size_t iDevNum = 0, count = g_wxmswDisplayInfoArray->Count(); - while ( iDevNum < count ) +wxArrayVideoModes wxDisplayMSW::GetModes(const wxVideoMode& modeMatch) const +{ + wxArrayVideoModes modes; + + // The first parameter of EnumDisplaySettings() must be NULL under Win95 + // according to MSDN. The version of GetName() we implement for Win95 + // returns an empty string. + const wxString name = GetName(); + const wxChar * const deviceName = name.empty() + ? (const wxChar*)NULL + : (const wxChar*)name.c_str(); + + DEVMODE dm; + dm.dmSize = sizeof(dm); + dm.dmDriverExtra = 0; + + for ( int iModeNum = 0; + ::EnumDisplaySettings(deviceName, iModeNum, &dm); + iModeNum++ ) { - wxmswDisplayInfo& info = (*g_wxmswDisplayInfoArray)[iDevNum]; + const wxVideoMode mode = ConvertToVideoMode(dm); + if ( mode.Matches(modeMatch) ) + { + modes.Add(mode); + } + } + + return modes; +} - // 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); +bool wxDisplayMSW::ChangeMode(const wxVideoMode& mode) +{ + // prepare ChangeDisplaySettingsEx() parameters + DEVMODE dm; + DEVMODE *pDevMode; - if ( !EnumDisplayDevices ( NULL, iDevNum, &info.m_dd, 0 ) ) - wxLogLastError(wxT("EnumDisplayDevices")); + int flags; - // get this display's Depth - DEVMODE devmode; - memset ( &devmode, 0, sizeof(devmode) ); + if ( mode == wxDefaultVideoMode ) + { + // reset the video mode to default + pDevMode = NULL; + flags = 0; + } + else // change to the given mode + { + wxCHECK_MSG( mode.GetWidth() && mode.GetHeight(), false, + wxT("at least the width and height must be specified") ); - // 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; + wxZeroMemory(dm); + dm.dmSize = sizeof(dm); + dm.dmDriverExtra = 0; + dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + dm.dmPelsWidth = mode.GetWidth(); + dm.dmPelsHeight = mode.GetHeight(); - if ( !EnumDisplaySettings ( info.m_dd.DeviceName, ENUM_CURRENT_SETTINGS, &devmode ) ) + if ( mode.GetDepth() ) { - wxLogLastError(wxT("EnumDisplaySettings")); - devmode.dmFields = 0; + dm.dmFields |= DM_BITSPERPEL; + dm.dmBitsPerPel = mode.GetDepth(); } - if ( !(devmode.dmFields&DM_BITSPERPEL) ) - info.m_depth = -1; - else - info.m_depth = devmode.dmBitsPerPel; + if ( mode.GetRefresh() ) + { + dm.dmFields |= DM_DISPLAYFREQUENCY; + dm.dmDisplayFrequency = mode.GetRefresh(); + } + pDevMode = &dm; - iDevNum++; +#ifdef __WXWINCE__ + flags = 0; +#else // !__WXWINCE__ + flags = CDS_FULLSCREEN; +#endif // __WXWINCE__/!__WXWINCE__ } - return TRUE; -} -void wxmswDisplayModule::OnExit() -{ - size_t count = g_wxmswDisplayInfoArray->Count(); - while ( count-- ) + + // 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 ) { - wxmswDisplayInfo* info = g_wxmswDisplayInfoArray->Detach ( count ); - delete info; + wxDynamicLibrary dllDisplay(displayDllName, wxDL_VERBATIM | wxDL_QUIET); + if ( dllDisplay.IsLoaded() ) + { + wxDL_INIT_FUNC_AW(pfn, ChangeDisplaySettingsEx, dllDisplay); + } + //else: huh, no this DLL must always be present, what's going on?? + +#ifndef __WXWINCE__ + if ( !pfnChangeDisplaySettingsEx ) + { + // we must be under Win95 and so there is no multiple monitors + // support anyhow + pfnChangeDisplaySettingsEx = ChangeDisplaySettingsExForWin95; + } +#endif // !__WXWINCE__ } - delete g_wxmswDisplayInfoArray; - g_wxmswDisplayInfoArray = 0; -} -// --------------------------------------------------------------------------- -// wxDisplay -// --------------------------------------------------------------------------- + // do change the mode + switch ( pfnChangeDisplaySettingsEx + ( + GetName().wx_str(), // 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 behaviour 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.GetWidth(), current.GetHeight()); + } + } + return true; + + case DISP_CHANGE_BADMODE: + // don't complain about this, this is the only "expected" error + break; + + default: + wxFAIL_MSG( wxT("unexpected ChangeDisplaySettingsEx() return value") ); + } -size_t wxDisplayBase::GetCount() -{ - return GetSystemMetrics ( SM_CMONITORS ); + return false; } -int wxDisplayBase::GetFromPoint ( const wxPoint& pt ) + +// ---------------------------------------------------------------------------- +// wxDisplayFactoryMSW implementation +// ---------------------------------------------------------------------------- + +wxDisplayFactoryMSW::wxDisplayFactoryMSW() { - POINT pt2; - pt2.x = pt.x; - pt2.y = pt.y; + if ( gs_MonitorFromPoint==NULL || gs_MonitorFromWindow==NULL + || gs_GetMonitorInfo==NULL || gs_EnumDisplayMonitors==NULL ) + { + // First initialization, or last initialization failed. + wxDynamicLibrary dllDisplay(displayDllName, wxDL_VERBATIM | wxDL_QUIET); + + wxDL_INIT_FUNC(gs_, MonitorFromPoint, dllDisplay); + wxDL_INIT_FUNC(gs_, MonitorFromWindow, dllDisplay); + wxDL_INIT_FUNC_AW(gs_, GetMonitorInfo, dllDisplay); + wxDL_INIT_FUNC(gs_, EnumDisplayMonitors, dllDisplay); + + // we can safely let dllDisplay go out of scope, the DLL itself will + // still remain loaded as all programs link to it statically anyhow + } - HMONITOR hmon = MonitorFromPoint ( pt2, 0 ); - if ( !hmon ) - return -1; - size_t count = wxDisplayBase::GetCount(), index; + if ( gs_MonitorFromPoint==NULL || gs_MonitorFromWindow==NULL + || gs_GetMonitorInfo==NULL || gs_EnumDisplayMonitors==NULL ) + return; - for ( index = 0; index < count; index++ ) + // enumerate all displays + if ( !gs_EnumDisplayMonitors(NULL, NULL, MultimonEnumProc, (LPARAM)this) ) { - if ( hmon == (*g_wxmswDisplayInfoArray)[index].m_hmon ) - return index; + wxLogLastError(wxT("EnumDisplayMonitors")); } +} + +/* static */ +BOOL CALLBACK +wxDisplayFactoryMSW::MultimonEnumProc( + HMONITOR hMonitor, // handle to display monitor + HDC WXUNUSED(hdcMonitor), // handle to monitor-appropriate device context + LPRECT WXUNUSED(lprcMonitor), // pointer to monitor intersection rectangle + LPARAM dwData) // data passed from EnumDisplayMonitors (this) +{ + wxDisplayFactoryMSW *const self = (wxDisplayFactoryMSW *)dwData; - return -1; + self->m_displays.Add(hMonitor); + + // continue the enumeration + return TRUE; } -wxDisplay::wxDisplay ( size_t index ) : wxDisplayBase ( index ) +wxDisplayImpl *wxDisplayFactoryMSW::CreateDisplay(unsigned n) { + wxCHECK_MSG( n < m_displays.size(), NULL, wxT("An invalid index was passed to wxDisplay") ); + + return new wxDisplayMSW(n, m_displays[n]); } -wxRect wxDisplay::GetGeometry() const +// helper for GetFromPoint() and GetFromWindow() +int wxDisplayFactoryMSW::FindDisplayFromHMONITOR(HMONITOR hmon) const { - return (*g_wxmswDisplayInfoArray)[m_index].m_rect; + if ( hmon ) + { + const size_t count = m_displays.size(); + for ( size_t n = 0; n < count; n++ ) + { + if ( hmon == m_displays[n] ) + return n; + } + } + + return wxNOT_FOUND; } -int wxDisplay::GetDepth() const +int wxDisplayFactoryMSW::GetFromPoint(const wxPoint& pt) { - return (*g_wxmswDisplayInfoArray)[m_index].m_depth; + POINT pt2; + pt2.x = pt.x; + pt2.y = pt.y; + + return FindDisplayFromHMONITOR(gs_MonitorFromPoint(pt2, + MONITOR_DEFAULTTONULL)); } -wxString wxDisplay::GetName() const +int wxDisplayFactoryMSW::GetFromWindow(const wxWindow *window) { - return wxString ( (*g_wxmswDisplayInfoArray)[m_index].m_dd.DeviceName ); + return FindDisplayFromHMONITOR(gs_MonitorFromWindow(GetHwndOf(window), + MONITOR_DEFAULTTONULL)); } -#endif//wxUSE_DISPLAY +#endif // wxUSE_DISPLAY