From 8585e2b5065cff7dfd733c8a4780dba91bde2cb2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 2 Mar 2003 14:17:20 +0000 Subject: [PATCH] implemented video mode support (getting the current one, enumerating, changing) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@19419 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/msw/display.h | 17 +- src/msw/display.cpp | 432 +++++++++++++++++++++++++++++---------- 2 files changed, 336 insertions(+), 113 deletions(-) diff --git a/include/wx/msw/display.h b/include/wx/msw/display.h index 766e4e3401..1508887ee7 100644 --- a/include/wx/msw/display.h +++ b/include/wx/msw/display.h @@ -9,18 +9,31 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// +#if defined(__GNUG__) && !defined(__APPLE__) + #pragma interface "display.h" +#endif + #ifndef _WX_MSW_DISPLAY_H_ #define _WX_MSW_DISPLAY_H_ class WXDLLEXPORT wxDisplay : public wxDisplayBase { public: - wxDisplay ( size_t index = 0 ); + wxDisplay(size_t index = 0); + // implement base class pure virtuals virtual wxRect GetGeometry() const; - virtual int GetDepth() const; virtual wxString GetName() const; + virtual wxArrayVideoModes + GetModes(const wxVideoMode& mode = wxVideoMode()) const; + virtual wxVideoMode GetCurrentMode() const; + virtual bool ChangeMode(const wxVideoMode& mode = wxVideoMode()); + +private: + // get the display name to use with EnumDisplaySettings() + wxString GetNameForEnumSettings() const; + DECLARE_NO_COPY_CLASS(wxDisplay); }; diff --git a/src/msw/display.cpp b/src/msw/display.cpp index cd5548c708..e85dd3e787 100644 --- a/src/msw/display.cpp +++ b/src/msw/display.cpp @@ -34,199 +34,409 @@ #include "wx/dynarray.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, this one doesn't compile without warnings + // with VC++ 6 + #pragma warning(disable:4706) #endif -// --------------------------------------------------------------------------- -// constants -// --------------------------------------------------------------------------- +#include -// --------------------------------------------------------------------------- -// private functions -// --------------------------------------------------------------------------- +#ifdef _MSC_VER + #pragma warning(default:4706) +#endif -void wxmswInitDisplayRectArray(); +#ifdef _UNICODE + #define MAKE_WFUNC(x) #x "W" + #define WINFUNC(x) L ## MAKE_WFUNC(x) +#else + #define WINFUNC(x) #x "A" +#endif // ---------------------------------------------------------------------------- -// private classes +// typedefs // ---------------------------------------------------------------------------- -class wxmswDisplayInfo; +typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName, + LPDEVMODE lpDevMode, + HWND hwnd, + DWORD dwFlags, + LPVOID lParam); -WX_DECLARE_OBJARRAY(wxmswDisplayInfo, wxmswDisplayInfoArray); +// ---------------------------------------------------------------------------- +// 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; + + // 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; } }; -wxmswDisplayInfoArray* g_wxmswDisplayInfoArray = 0; +WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray); +#include "wx/arrimpl.cpp" +WX_DEFINE_OBJARRAY(wxDisplayInfoArray); -#include // this is a magic incantation which must be done! -WX_DEFINE_OBJARRAY(wxmswDisplayInfoArray); +// 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 +class wxDisplayModule : public wxModule +{ +public: + virtual bool OnInit() { return TRUE; } + virtual void OnExit(); + + DECLARE_DYNAMIC_CLASS(wxDisplayModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule, wxModule) // =========================================================================== // implementation // =========================================================================== -BOOL CALLBACK wxmswMonitorEnumProc ( +// ---------------------------------------------------------------------------- +// local functions +// ---------------------------------------------------------------------------- + +static BOOL CALLBACK wxmswMonitorEnumProc ( HMONITOR hMonitor, // handle to display monitor - HDC hdcMonitor, // handle to monitor-appropriate device context + HDC hdcMonitor, // handle to monitor-appropriate device context (NULL) LPRECT lprcMonitor, // pointer to monitor intersection rectangle - LPARAM dwData // data passed from EnumDisplayMonitors + LPARAM 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 ); + g_displays->Add(info); - return TRUE; // continue the enumeration + // continue the enumeration + return TRUE; } -class wxmswDisplayModule : public wxModule +// this function must be called before accessing g_displays array as it +// creates and initializes it +static void InitDisplays() { - DECLARE_DYNAMIC_CLASS(wxmswDisplayModule) -public: - wxmswDisplayModule() {} - bool OnInit(); - void OnExit(); -}; + if ( g_displays ) + return; -IMPLEMENT_DYNAMIC_CLASS(wxmswDisplayModule, wxModule) + g_displays = new wxDisplayInfoArray(); -bool wxmswDisplayModule::OnInit() -{ - g_wxmswDisplayInfoArray = new wxmswDisplayInfoArray(); - if ( !g_wxmswDisplayInfoArray ) + // enumerate all displays + if ( !::EnumDisplayMonitors(NULL, NULL, wxmswMonitorEnumProc, 0) ) { - wxFAIL_MSG(wxT("Couldn't allocate array for display information")); - return FALSE; + wxLogLastError(wxT("EnumDisplayMonitors")); + + // TODO: still create at least one (valid) entry in g_displays for the + // primary display! } +} - // 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")); +// 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); +} - size_t iDevNum = 0, count = g_wxmswDisplayInfoArray->Count(); - while ( iDevNum < count ) - { - wxmswDisplayInfo& info = (*g_wxmswDisplayInfoArray)[iDevNum]; +// 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); +} - // 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); +// ---------------------------------------------------------------------------- +// wxDisplayModule +// ---------------------------------------------------------------------------- - if ( !EnumDisplayDevices ( NULL, iDevNum, &info.m_dd, 0 ) ) - wxLogLastError(wxT("EnumDisplayDevices")); +void wxDisplayModule::OnExit() +{ + delete g_displays; +} - // get this display's Depth - DEVMODE devmode; - memset ( &devmode, 0, sizeof(devmode) ); +// --------------------------------------------------------------------------- +// wxDisplay +// --------------------------------------------------------------------------- - // 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; +// helper of GetFromPoint() and GetFromWindow() +static int DisplayFromHMONITOR(HMONITOR hmon) +{ + if ( hmon ) + { + const size_t count = wxDisplay::GetCount(); - if ( !EnumDisplaySettings ( info.m_dd.DeviceName, ENUM_CURRENT_SETTINGS, &devmode ) ) + for ( size_t n = 0; n < count; n++ ) { - wxLogLastError(wxT("EnumDisplaySettings")); - devmode.dmFields = 0; + if ( hmon == (*g_displays)[n].m_hmon ) + return n; } - - if ( !(devmode.dmFields&DM_BITSPERPEL) ) - info.m_depth = -1; - else - info.m_depth = devmode.dmBitsPerPel; - - - iDevNum++; } - return TRUE; -} -void wxmswDisplayModule::OnExit() -{ - size_t count = g_wxmswDisplayInfoArray->Count(); - while ( count-- ) - { - wxmswDisplayInfo* info = g_wxmswDisplayInfoArray->Detach ( count ); - delete info; - } - delete g_wxmswDisplayInfoArray; - g_wxmswDisplayInfoArray = 0; + return wxNOT_FOUND; } -// --------------------------------------------------------------------------- -// wxDisplay -// --------------------------------------------------------------------------- - +/* static */ size_t wxDisplayBase::GetCount() { - return GetSystemMetrics ( SM_CMONITORS ); + 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?") ); + + return g_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)); +} - for ( index = 0; index < count; index++ ) +/* static */ +int wxDisplayBase::GetFromWindow(wxWindow *window) +{ + return DisplayFromHMONITOR + ( + ::MonitorFromWindow(GetHwndOf(window), MONITOR_DEFAULTTONULL) + ); +} + +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(); +} + +wxRect wxDisplay::GetGeometry() const +{ + return (*g_displays)[m_index].m_rect; +} + +wxString wxDisplay::GetName() const +{ + wxDisplayInfo& dpyInfo = (*g_displays)[m_index]; + if ( dpyInfo.m_devName.empty() ) { - if ( hmon == (*g_wxmswDisplayInfoArray)[index].m_hmon ) - return index; + MONITORINFOEX monInfo; + wxZeroMemory(monInfo); + monInfo.cbSize = sizeof(monInfo); + + if ( !::GetMonitorInfo(dpyInfo.m_hmon, &monInfo) ) + { + wxLogLastError(_T("GetMonitorInfo")); + } + else + { + dpyInfo.m_devName = monInfo.szDevice; + } } - return -1; + return dpyInfo.m_devName; } -wxDisplay::wxDisplay ( size_t index ) : wxDisplayBase ( index ) +wxString wxDisplay::GetNameForEnumSettings() const { + 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; } -wxRect wxDisplay::GetGeometry() const +wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const { - return (*g_wxmswDisplayInfoArray)[m_index].m_rect; + 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; } -int wxDisplay::GetDepth() const +wxVideoMode wxDisplay::GetCurrentMode() const { - return (*g_wxmswDisplayInfoArray)[m_index].m_depth; + 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; } -wxString wxDisplay::GetName() const +bool wxDisplay::ChangeMode(const wxVideoMode& mode) { - return wxString ( (*g_wxmswDisplayInfoArray)[m_index].m_dd.DeviceName ); + // 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 + 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; } -#endif//wxUSE_DISPLAY +#endif // wxUSE_DISPLAY + -- 2.47.2