implemented video mode support (getting the current one, enumerating, changing)
[wxWidgets.git] / src / msw / display.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: display.cpp
3 // Purpose: MSW Implementation of wxDisplay class
4 // Author: Royce Mitchell III
5 // Modified by:
6 // Created: 06/21/02
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWindows team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "display.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_DISPLAY
32
33 #ifndef WX_PRECOMP
34 #include "wx/dynarray.h"
35 #endif
36
37 #include "wx/dynload.h"
38
39 #include "wx/display.h"
40
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
44
45 // if you don't have multimon.h you can download the file from:
46 //
47 // http://www.microsoft.com/msj/0697/monitor/monitortextfigs.htm#fig4
48 //
49
50 #ifdef _MSC_VER
51 // as (m)any standard header, this one doesn't compile without warnings
52 // with VC++ 6 <sigh>
53 #pragma warning(disable:4706)
54 #endif
55
56 #include <multimon.h>
57
58 #ifdef _MSC_VER
59 #pragma warning(default:4706)
60 #endif
61
62 #ifdef _UNICODE
63 #define MAKE_WFUNC(x) #x "W"
64 #define WINFUNC(x) L ## MAKE_WFUNC(x)
65 #else
66 #define WINFUNC(x) #x "A"
67 #endif
68
69 // ----------------------------------------------------------------------------
70 // typedefs
71 // ----------------------------------------------------------------------------
72
73 typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName,
74 LPDEVMODE lpDevMode,
75 HWND hwnd,
76 DWORD dwFlags,
77 LPVOID lParam);
78
79 // ----------------------------------------------------------------------------
80 // private classes
81 // ----------------------------------------------------------------------------
82
83 class wxDisplayInfo
84 {
85 public:
86 // handle of this monitor used by MonitorXXX() functions, never NULL
87 HMONITOR m_hmon;
88
89 // the entire area of this monitor in virtual screen coordinates
90 wxRect m_rect;
91
92 // the display device name for this monitor, empty initially and retrieved
93 // on demand by DoGetName()
94 wxString m_devName;
95
96 wxDisplayInfo() { m_hmon = NULL; }
97 };
98
99 WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray);
100 #include "wx/arrimpl.cpp"
101 WX_DEFINE_OBJARRAY(wxDisplayInfoArray);
102
103 // this is not really MT-unsafe as wxDisplay is only going to be used from the
104 // main thread, i.e. we consider that it's a GUI class and so don't protect it
105 static wxDisplayInfoArray *g_displays = NULL;
106
107
108 // this module is used to cleanup g_displays array
109 class wxDisplayModule : public wxModule
110 {
111 public:
112 virtual bool OnInit() { return TRUE; }
113 virtual void OnExit();
114
115 DECLARE_DYNAMIC_CLASS(wxDisplayModule)
116 };
117
118 IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule, wxModule)
119
120 // ===========================================================================
121 // implementation
122 // ===========================================================================
123
124 // ----------------------------------------------------------------------------
125 // local functions
126 // ----------------------------------------------------------------------------
127
128 static BOOL CALLBACK wxmswMonitorEnumProc (
129 HMONITOR hMonitor, // handle to display monitor
130 HDC hdcMonitor, // handle to monitor-appropriate device context (NULL)
131 LPRECT lprcMonitor, // pointer to monitor intersection rectangle
132 LPARAM dwData // data passed from EnumDisplayMonitors (unused)
133 )
134 {
135 wxDisplayInfo* info = new wxDisplayInfo();
136
137 // we need hMonitor to be able to map display id to it which is needed for
138 // MonitorXXX() functions, in particular MonitorFromPoint()
139 info->m_hmon = hMonitor;
140
141 // we also store the display geometry
142 info->m_rect.SetX ( lprcMonitor->left );
143 info->m_rect.SetY ( lprcMonitor->top );
144 info->m_rect.SetWidth ( lprcMonitor->right - lprcMonitor->left );
145 info->m_rect.SetHeight ( lprcMonitor->bottom - lprcMonitor->top );
146
147 // now add this monitor to the array
148 g_displays->Add(info);
149
150 // continue the enumeration
151 return TRUE;
152 }
153
154 // this function must be called before accessing g_displays array as it
155 // creates and initializes it
156 static void InitDisplays()
157 {
158 if ( g_displays )
159 return;
160
161 g_displays = new wxDisplayInfoArray();
162
163 // enumerate all displays
164 if ( !::EnumDisplayMonitors(NULL, NULL, wxmswMonitorEnumProc, 0) )
165 {
166 wxLogLastError(wxT("EnumDisplayMonitors"));
167
168 // TODO: still create at least one (valid) entry in g_displays for the
169 // primary display!
170 }
171 }
172
173 // convert a DEVMODE to our wxVideoMode
174 wxVideoMode ConvertToVideoMode(const DEVMODE& dm)
175 {
176 // note that dmDisplayFrequency may be 0 or 1 meaning "standard one" and
177 // although 0 is ok for us we don't want to return modes with 1hz refresh
178 return wxVideoMode(dm.dmPelsWidth,
179 dm.dmPelsHeight,
180 dm.dmBitsPerPel,
181 dm.dmDisplayFrequency > 1 ? dm.dmDisplayFrequency : 0);
182 }
183
184 // emulation of ChangeDisplaySettingsEx() for Win95
185 LONG WINAPI ChangeDisplaySettingsExForWin95(LPCTSTR WXUNUSED(lpszDeviceName),
186 LPDEVMODE lpDevMode,
187 HWND WXUNUSED(hwnd),
188 DWORD dwFlags,
189 LPVOID WXUNUSED(lParam))
190 {
191 return ::ChangeDisplaySettings(lpDevMode, dwFlags);
192 }
193
194 // ----------------------------------------------------------------------------
195 // wxDisplayModule
196 // ----------------------------------------------------------------------------
197
198 void wxDisplayModule::OnExit()
199 {
200 delete g_displays;
201 }
202
203 // ---------------------------------------------------------------------------
204 // wxDisplay
205 // ---------------------------------------------------------------------------
206
207 // helper of GetFromPoint() and GetFromWindow()
208 static int DisplayFromHMONITOR(HMONITOR hmon)
209 {
210 if ( hmon )
211 {
212 const size_t count = wxDisplay::GetCount();
213
214 for ( size_t n = 0; n < count; n++ )
215 {
216 if ( hmon == (*g_displays)[n].m_hmon )
217 return n;
218 }
219 }
220
221 return wxNOT_FOUND;
222 }
223
224 /* static */
225 size_t wxDisplayBase::GetCount()
226 {
227 InitDisplays();
228
229 // I'm not sure if they really always return the same thing and if this is
230 // not true I'd like to know in which situation does it happen
231 wxASSERT_MSG( g_displays->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS),
232 _T("So how many displays does this system have?") );
233
234 return g_displays->GetCount();
235 }
236
237 /* static */
238 int wxDisplayBase::GetFromPoint ( const wxPoint& pt )
239 {
240 POINT pt2;
241 pt2.x = pt.x;
242 pt2.y = pt.y;
243
244 return DisplayFromHMONITOR(::MonitorFromPoint(pt2, MONITOR_DEFAULTTONULL));
245 }
246
247 /* static */
248 int wxDisplayBase::GetFromWindow(wxWindow *window)
249 {
250 return DisplayFromHMONITOR
251 (
252 ::MonitorFromWindow(GetHwndOf(window), MONITOR_DEFAULTTONULL)
253 );
254 }
255
256 wxDisplay::wxDisplay ( size_t n )
257 : wxDisplayBase ( n )
258 {
259 // if we do this in ctor we won't have to call it from all the member
260 // functions
261 InitDisplays();
262 }
263
264 wxRect wxDisplay::GetGeometry() const
265 {
266 return (*g_displays)[m_index].m_rect;
267 }
268
269 wxString wxDisplay::GetName() const
270 {
271 wxDisplayInfo& dpyInfo = (*g_displays)[m_index];
272 if ( dpyInfo.m_devName.empty() )
273 {
274 MONITORINFOEX monInfo;
275 wxZeroMemory(monInfo);
276 monInfo.cbSize = sizeof(monInfo);
277
278 if ( !::GetMonitorInfo(dpyInfo.m_hmon, &monInfo) )
279 {
280 wxLogLastError(_T("GetMonitorInfo"));
281 }
282 else
283 {
284 dpyInfo.m_devName = monInfo.szDevice;
285 }
286 }
287
288 return dpyInfo.m_devName;
289 }
290
291 wxString wxDisplay::GetNameForEnumSettings() const
292 {
293 int major, minor;
294 const bool isWin95 = wxGetOsVersion(&major, &minor) == wxWIN95 &&
295 major == 4 && minor == 0;
296
297 // the first parameter of EnumDisplaySettings() must be NULL under Win95
298 // according to MSDN but GetMonitorInfo() stub in multimon.h still returns
299 // something even in this case, so we have to correct this manually
300 wxString name;
301 if ( !isWin95 )
302 name = GetName();
303
304 return name;
305 }
306
307 wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const
308 {
309 wxArrayVideoModes modes;
310
311 const wxString name = GetNameForEnumSettings();
312
313 const wxChar * const deviceName = name.empty() ? NULL : name.c_str();
314
315 DEVMODE dm;
316 for ( int iModeNum = 0;
317 ::EnumDisplaySettings(deviceName, iModeNum, &dm);
318 iModeNum++ )
319 {
320 const wxVideoMode mode = ConvertToVideoMode(dm);
321 if ( mode.Matches(modeMatch) )
322 {
323 modes.Add(mode);
324 }
325 }
326
327 return modes;
328 }
329
330 wxVideoMode wxDisplay::GetCurrentMode() const
331 {
332 wxVideoMode mode;
333
334 const wxString name = GetNameForEnumSettings();
335
336 DEVMODE dm;
337 if ( !::EnumDisplaySettings(name.empty() ? NULL : name.c_str(),
338 ENUM_CURRENT_SETTINGS,
339 &dm) )
340 {
341 wxLogLastError(_T("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
342 }
343 else
344 {
345 mode = ConvertToVideoMode(dm);
346 }
347
348 return mode;
349 }
350
351 bool wxDisplay::ChangeMode(const wxVideoMode& mode)
352 {
353 // prepare ChangeDisplaySettingsEx() parameters
354 DEVMODE dm,
355 *pDevMode;
356 int flags;
357
358 if ( mode == wxDefaultVideoMode )
359 {
360 // reset the video mode to default
361 pDevMode = NULL;
362 flags = 0;
363 }
364 else // change to the given mode
365 {
366 wxCHECK_MSG( mode.w && mode.h, FALSE,
367 _T("at least the width and height must be specified") );
368
369 wxZeroMemory(dm);
370 dm.dmSize = sizeof(dm);
371 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
372 dm.dmPelsWidth = mode.w;
373 dm.dmPelsHeight = mode.h;
374
375 if ( mode.bpp )
376 {
377 dm.dmFields |= DM_BITSPERPEL;
378 dm.dmBitsPerPel = mode.bpp;
379 }
380
381 if ( mode.refresh )
382 {
383 dm.dmFields |= DM_DISPLAYFREQUENCY;
384 dm.dmDisplayFrequency = mode.refresh;
385 }
386
387 pDevMode = &dm;
388
389 flags = CDS_FULLSCREEN;
390 }
391
392
393 // get pointer to the function dynamically
394 //
395 // we're only called from the main thread, so it's ok to use static
396 // variable
397 static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx = NULL;
398 if ( !pfnChangeDisplaySettingsEx )
399 {
400 wxDynamicLibrary dllUser32(_T("user32.dll"));
401 if ( dllUser32.IsLoaded() )
402 {
403 pfnChangeDisplaySettingsEx = (ChangeDisplaySettingsEx_t)
404 dllUser32.GetSymbol(WINFUNC(ChangeDisplaySettingsEx));
405 }
406 //else: huh, no user32.dll??
407
408 if ( !pfnChangeDisplaySettingsEx )
409 {
410 // we must be under Win95 and so there is no multiple monitors
411 // support anyhow
412 pfnChangeDisplaySettingsEx = ChangeDisplaySettingsExForWin95;
413 }
414 }
415
416 // do change the mode
417 switch ( pfnChangeDisplaySettingsEx
418 (
419 GetName(), // display name
420 pDevMode, // dev mode or NULL to reset
421 NULL, // reserved
422 flags,
423 NULL // pointer to video parameters (not used)
424 ) )
425 {
426 case DISP_CHANGE_SUCCESSFUL:
427 // ok
428 return TRUE;
429
430 case DISP_CHANGE_BADMODE:
431 // don't complain about this, this is the only "expected" error
432 break;
433
434 default:
435 wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") );
436 }
437
438 return FALSE;
439 }
440
441 #endif // wxUSE_DISPLAY
442