wxDisplay only needs dynlib.h, not dynload.h.
[wxWidgets.git] / src / msw / display.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/display.cpp
3 // Purpose: MSW Implementation of wxDisplay class
4 // Author: Royce Mitchell III, Vadim Zeitlin
5 // Modified by: Ryan Norton (IsPrimary override)
6 // Created: 06/21/02
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets team
9 // Copyright: (c) 2002-2006 wxWidgets team
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 // ===========================================================================
14 // declarations
15 // ===========================================================================
16
17 // ---------------------------------------------------------------------------
18 // headers
19 // ---------------------------------------------------------------------------
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #if wxUSE_DISPLAY
29
30 #include "wx/display.h"
31
32 #ifndef WX_PRECOMP
33 #include "wx/dynarray.h"
34 #include "wx/app.h"
35 #include "wx/frame.h"
36 #endif
37
38 #include "wx/dynlib.h"
39 #include "wx/sysopt.h"
40
41 #include "wx/display_impl.h"
42 #include "wx/msw/wrapwin.h"
43 #include "wx/msw/missing.h"
44 #include "wx/msw/private.h"
45 #include "wx/msw/private/hiddenwin.h"
46
47 #ifndef __WXWINCE__
48 // Older versions of windef.h don't define HMONITOR. Unfortunately, we
49 // can't directly test whether HMONITOR is defined or not in windef.h as
50 // it's not a macro but a typedef, so we test for an unrelated symbol which
51 // is only defined in winuser.h if WINVER >= 0x0500
52 #if !defined(HMONITOR_DECLARED) && !defined(MNS_NOCHECK)
53 DECLARE_HANDLE(HMONITOR);
54 typedef BOOL(CALLBACK * MONITORENUMPROC )(HMONITOR, HDC, LPRECT, LPARAM);
55 typedef struct tagMONITORINFO
56 {
57 DWORD cbSize;
58 RECT rcMonitor;
59 RECT rcWork;
60 DWORD dwFlags;
61 } MONITORINFO, *LPMONITORINFO;
62 typedef struct tagMONITORINFOEX : public tagMONITORINFO
63 {
64 TCHAR szDevice[CCHDEVICENAME];
65 } MONITORINFOEX, *LPMONITORINFOEX;
66 #define MONITOR_DEFAULTTONULL 0x00000000
67 #define MONITOR_DEFAULTTOPRIMARY 0x00000001
68 #define MONITOR_DEFAULTTONEAREST 0x00000002
69 #define MONITORINFOF_PRIMARY 0x00000001
70 #define HMONITOR_DECLARED
71 #endif
72 #endif // !__WXWINCE__
73
74 // display functions are found in different DLLs under WinCE and normal Win32
75 #ifdef __WXWINCE__
76 static const wxChar displayDllName[] = wxT("coredll.dll");
77 #else
78 static const wxChar displayDllName[] = wxT("user32.dll");
79 #endif
80
81 // ----------------------------------------------------------------------------
82 // typedefs for dynamically loaded Windows functions
83 // ----------------------------------------------------------------------------
84
85 typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName,
86 LPDEVMODE lpDevMode,
87 HWND hwnd,
88 DWORD dwFlags,
89 LPVOID lParam);
90
91 typedef BOOL (WINAPI *EnumDisplayMonitors_t)(HDC,LPCRECT,MONITORENUMPROC,LPARAM);
92 typedef HMONITOR (WINAPI *MonitorFromPoint_t)(POINT,DWORD);
93 typedef HMONITOR (WINAPI *MonitorFromWindow_t)(HWND,DWORD);
94 typedef BOOL (WINAPI *GetMonitorInfo_t)(HMONITOR,LPMONITORINFO);
95
96 #ifndef __WXWINCE__
97 // emulation of ChangeDisplaySettingsEx() for Win95
98 LONG WINAPI ChangeDisplaySettingsExForWin95(LPCTSTR WXUNUSED(lpszDeviceName),
99 LPDEVMODE lpDevMode,
100 HWND WXUNUSED(hwnd),
101 DWORD dwFlags,
102 LPVOID WXUNUSED(lParam))
103 {
104 return ::ChangeDisplaySettings(lpDevMode, dwFlags);
105 }
106 #endif // !__WXWINCE__
107
108
109 // ----------------------------------------------------------------------------
110 // wxDisplayMSW declaration
111 // ----------------------------------------------------------------------------
112
113 class wxDisplayMSW : public wxDisplayImpl
114 {
115 public:
116 wxDisplayMSW(unsigned n, HMONITOR hmon)
117 : wxDisplayImpl(n),
118 m_hmon(hmon)
119 {
120 }
121
122 virtual wxRect GetGeometry() const;
123 virtual wxRect GetClientArea() const;
124 virtual wxString GetName() const;
125 virtual bool IsPrimary() const;
126
127 virtual wxVideoMode GetCurrentMode() const;
128 virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const;
129 virtual bool ChangeMode(const wxVideoMode& mode);
130
131 protected:
132 // convert a DEVMODE to our wxVideoMode
133 static wxVideoMode ConvertToVideoMode(const DEVMODE& dm)
134 {
135 // note that dmDisplayFrequency may be 0 or 1 meaning "standard one"
136 // and although 0 is ok for us we don't want to return modes with 1hz
137 // refresh
138 return wxVideoMode(dm.dmPelsWidth,
139 dm.dmPelsHeight,
140 dm.dmBitsPerPel,
141 dm.dmDisplayFrequency > 1 ? dm.dmDisplayFrequency : 0);
142 }
143
144 // Call GetMonitorInfo() and fill in the provided struct and return true if
145 // it succeeded, otherwise return false.
146 bool GetMonInfo(MONITORINFOEX& monInfo) const;
147
148 HMONITOR m_hmon;
149
150 private:
151 wxDECLARE_NO_COPY_CLASS(wxDisplayMSW);
152 };
153
154
155 // ----------------------------------------------------------------------------
156 // wxDisplayFactoryMSW declaration
157 // ----------------------------------------------------------------------------
158
159 WX_DEFINE_ARRAY(HMONITOR, wxMonitorHandleArray);
160
161 // functions dynamically bound by wxDisplayFactoryMSW ctor.
162 static MonitorFromPoint_t gs_MonitorFromPoint = NULL;
163 static MonitorFromWindow_t gs_MonitorFromWindow = NULL;
164 static GetMonitorInfo_t gs_GetMonitorInfo = NULL;
165 static EnumDisplayMonitors_t gs_EnumDisplayMonitors = NULL;
166
167 class wxDisplayFactoryMSW : public wxDisplayFactory
168 {
169 public:
170 // ctor checks if the current system supports multimon API and dynamically
171 // bind the functions we need if this is the case and fills the
172 // m_displays array if they're available
173 wxDisplayFactoryMSW();
174
175 // Dtor destroys the hidden window we use for getting WM_SETTINGCHANGE.
176 virtual ~wxDisplayFactoryMSW();
177
178 bool IsOk() const { return !m_displays.empty(); }
179
180 virtual wxDisplayImpl *CreateDisplay(unsigned n);
181 virtual unsigned GetCount() { return unsigned(m_displays.size()); }
182 virtual int GetFromPoint(const wxPoint& pt);
183 virtual int GetFromWindow(const wxWindow *window);
184
185 // Called when we receive WM_SETTINGCHANGE to refresh the list of monitor
186 // handles.
187 static void RefreshMonitors() { ms_factory->DoRefreshMonitors(); }
188
189
190 private:
191 // EnumDisplayMonitors() callback
192 static BOOL CALLBACK MultimonEnumProc(HMONITOR hMonitor,
193 HDC hdcMonitor,
194 LPRECT lprcMonitor,
195 LPARAM dwData);
196
197 // find the monitor corresponding to the given handle,
198 // return wxNOT_FOUND if not found
199 int FindDisplayFromHMONITOR(HMONITOR hmon) const;
200
201 // Update m_displays array, used by RefreshMonitors().
202 void DoRefreshMonitors();
203
204
205 // The unique factory being used (as we don't have direct access to the
206 // global factory pointer in the common code so we just duplicate this
207 // variable (also making it of correct type for us) here).
208 static wxDisplayFactoryMSW* ms_factory;
209
210
211 // the array containing information about all available displays, filled by
212 // MultimonEnumProc()
213 wxMonitorHandleArray m_displays;
214
215 // The hidden window we use for receiving WM_SETTINGCHANGE and its class
216 // name.
217 HWND m_hiddenHwnd;
218 const wxChar* m_hiddenClass;
219
220 wxDECLARE_NO_COPY_CLASS(wxDisplayFactoryMSW);
221 };
222
223 wxDisplayFactoryMSW* wxDisplayFactoryMSW::ms_factory = NULL;
224
225 // ----------------------------------------------------------------------------
226 // wxDisplay implementation
227 // ----------------------------------------------------------------------------
228
229 /* static */ wxDisplayFactory *wxDisplay::CreateFactory()
230 {
231 wxDisplayFactoryMSW *factoryMM = new wxDisplayFactoryMSW;
232
233 if ( factoryMM->IsOk() )
234 return factoryMM;
235
236 delete factoryMM;
237
238 // fall back to a stub implementation if no multimon support (Win95?)
239 return new wxDisplayFactorySingle;
240 }
241
242
243 // ----------------------------------------------------------------------------
244 // wxDisplayMSW implementation
245 // ----------------------------------------------------------------------------
246
247 bool wxDisplayMSW::GetMonInfo(MONITORINFOEX& monInfo) const
248 {
249 if ( !gs_GetMonitorInfo(m_hmon, &monInfo) )
250 {
251 wxLogLastError(wxT("GetMonitorInfo"));
252 return false;
253 }
254
255 return true;
256 }
257
258 wxRect wxDisplayMSW::GetGeometry() const
259 {
260 WinStruct<MONITORINFOEX> monInfo;
261
262 wxRect rect;
263 if ( GetMonInfo(monInfo) )
264 wxCopyRECTToRect(monInfo.rcMonitor, rect);
265
266 return rect;
267 }
268
269 wxRect wxDisplayMSW::GetClientArea() const
270 {
271 WinStruct<MONITORINFOEX> monInfo;
272
273 wxRect rectClient;
274 if ( GetMonInfo(monInfo) )
275 wxCopyRECTToRect(monInfo.rcWork, rectClient);
276
277 return rectClient;
278 }
279
280 wxString wxDisplayMSW::GetName() const
281 {
282 WinStruct<MONITORINFOEX> monInfo;
283
284 wxString name;
285 if ( GetMonInfo(monInfo) )
286 name = monInfo.szDevice;
287
288 return name;
289 }
290
291 bool wxDisplayMSW::IsPrimary() const
292 {
293 WinStruct<MONITORINFOEX> monInfo;
294
295 if ( !GetMonInfo(monInfo) )
296 return false;
297
298 return (monInfo.dwFlags & MONITORINFOF_PRIMARY) != 0;
299 }
300
301 wxVideoMode wxDisplayMSW::GetCurrentMode() const
302 {
303 wxVideoMode mode;
304
305 // The first parameter of EnumDisplaySettings() must be NULL under Win95
306 // according to MSDN. The version of GetName() we implement for Win95
307 // returns an empty string.
308 const wxString name = GetName();
309 const wxChar * const deviceName = name.empty()
310 ? (const wxChar*)NULL
311 : (const wxChar*)name.c_str();
312
313 DEVMODE dm;
314 dm.dmSize = sizeof(dm);
315 dm.dmDriverExtra = 0;
316
317 if ( !::EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, &dm) )
318 {
319 wxLogLastError(wxT("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
320 }
321 else
322 {
323 mode = ConvertToVideoMode(dm);
324 }
325
326 return mode;
327 }
328
329 wxArrayVideoModes wxDisplayMSW::GetModes(const wxVideoMode& modeMatch) const
330 {
331 wxArrayVideoModes modes;
332
333 // The first parameter of EnumDisplaySettings() must be NULL under Win95
334 // according to MSDN. The version of GetName() we implement for Win95
335 // returns an empty string.
336 const wxString name = GetName();
337 const wxChar * const deviceName = name.empty()
338 ? (const wxChar*)NULL
339 : (const wxChar*)name.c_str();
340
341 DEVMODE dm;
342 dm.dmSize = sizeof(dm);
343 dm.dmDriverExtra = 0;
344
345 for ( int iModeNum = 0;
346 ::EnumDisplaySettings(deviceName, iModeNum, &dm);
347 iModeNum++ )
348 {
349 const wxVideoMode mode = ConvertToVideoMode(dm);
350 if ( mode.Matches(modeMatch) )
351 {
352 modes.Add(mode);
353 }
354 }
355
356 return modes;
357 }
358
359 bool wxDisplayMSW::ChangeMode(const wxVideoMode& mode)
360 {
361 // prepare ChangeDisplaySettingsEx() parameters
362 DEVMODE dm;
363 DEVMODE *pDevMode;
364
365 int flags;
366
367 if ( mode == wxDefaultVideoMode )
368 {
369 // reset the video mode to default
370 pDevMode = NULL;
371 flags = 0;
372 }
373 else // change to the given mode
374 {
375 wxCHECK_MSG( mode.GetWidth() && mode.GetHeight(), false,
376 wxT("at least the width and height must be specified") );
377
378 wxZeroMemory(dm);
379 dm.dmSize = sizeof(dm);
380 dm.dmDriverExtra = 0;
381 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
382 dm.dmPelsWidth = mode.GetWidth();
383 dm.dmPelsHeight = mode.GetHeight();
384
385 if ( mode.GetDepth() )
386 {
387 dm.dmFields |= DM_BITSPERPEL;
388 dm.dmBitsPerPel = mode.GetDepth();
389 }
390
391 if ( mode.GetRefresh() )
392 {
393 dm.dmFields |= DM_DISPLAYFREQUENCY;
394 dm.dmDisplayFrequency = mode.GetRefresh();
395 }
396
397 pDevMode = &dm;
398
399 #ifdef __WXWINCE__
400 flags = 0;
401 #else // !__WXWINCE__
402 flags = CDS_FULLSCREEN;
403 #endif // __WXWINCE__/!__WXWINCE__
404 }
405
406
407 // get pointer to the function dynamically
408 //
409 // we're only called from the main thread, so it's ok to use static
410 // variable
411 static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx = NULL;
412 if ( !pfnChangeDisplaySettingsEx )
413 {
414 wxDynamicLibrary dllDisplay(displayDllName, wxDL_VERBATIM | wxDL_QUIET);
415 if ( dllDisplay.IsLoaded() )
416 {
417 wxDL_INIT_FUNC_AW(pfn, ChangeDisplaySettingsEx, dllDisplay);
418 }
419 //else: huh, no this DLL must always be present, what's going on??
420
421 #ifndef __WXWINCE__
422 if ( !pfnChangeDisplaySettingsEx )
423 {
424 // we must be under Win95 and so there is no multiple monitors
425 // support anyhow
426 pfnChangeDisplaySettingsEx = ChangeDisplaySettingsExForWin95;
427 }
428 #endif // !__WXWINCE__
429 }
430
431 // do change the mode
432 switch ( pfnChangeDisplaySettingsEx
433 (
434 GetName().t_str(), // display name
435 pDevMode, // dev mode or NULL to reset
436 NULL, // reserved
437 flags,
438 NULL // pointer to video parameters (not used)
439 ) )
440 {
441 case DISP_CHANGE_SUCCESSFUL:
442 // ok
443 {
444 // If we have a top-level, full-screen frame, emulate
445 // the DirectX behaviour and resize it. This makes this
446 // API quite a bit easier to use.
447 wxWindow *winTop = wxTheApp->GetTopWindow();
448 wxFrame *frameTop = wxDynamicCast(winTop, wxFrame);
449 if (frameTop && frameTop->IsFullScreen())
450 {
451 wxVideoMode current = GetCurrentMode();
452 frameTop->SetClientSize(current.GetWidth(), current.GetHeight());
453 }
454 }
455 return true;
456
457 case DISP_CHANGE_BADMODE:
458 // don't complain about this, this is the only "expected" error
459 break;
460
461 default:
462 wxFAIL_MSG( wxT("unexpected ChangeDisplaySettingsEx() return value") );
463 }
464
465 return false;
466 }
467
468
469 // ----------------------------------------------------------------------------
470 // wxDisplayFactoryMSW implementation
471 // ----------------------------------------------------------------------------
472
473 LRESULT APIENTRY
474 wxDisplayWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
475 {
476 if ( msg == WM_SETTINGCHANGE )
477 {
478 wxDisplayFactoryMSW::RefreshMonitors();
479
480 return 0;
481 }
482
483 return ::DefWindowProc(hwnd, msg, wParam, lParam);
484 }
485
486 wxDisplayFactoryMSW::wxDisplayFactoryMSW()
487 {
488 // This is not supposed to happen with the current code, the factory is
489 // implicitly a singleton.
490 wxASSERT_MSG( !ms_factory, wxS("Using more than one factory?") );
491
492 ms_factory = this;
493
494 m_hiddenHwnd = NULL;
495 m_hiddenClass = NULL;
496
497 if ( gs_MonitorFromPoint==NULL || gs_MonitorFromWindow==NULL
498 || gs_GetMonitorInfo==NULL || gs_EnumDisplayMonitors==NULL )
499 {
500 // First initialization, or last initialization failed.
501 wxDynamicLibrary dllDisplay(displayDllName, wxDL_VERBATIM | wxDL_QUIET);
502
503 wxDL_INIT_FUNC(gs_, MonitorFromPoint, dllDisplay);
504 wxDL_INIT_FUNC(gs_, MonitorFromWindow, dllDisplay);
505 wxDL_INIT_FUNC_AW(gs_, GetMonitorInfo, dllDisplay);
506 wxDL_INIT_FUNC(gs_, EnumDisplayMonitors, dllDisplay);
507
508 // we can safely let dllDisplay go out of scope, the DLL itself will
509 // still remain loaded as all programs link to it statically anyhow
510 }
511
512 if ( gs_MonitorFromPoint==NULL || gs_MonitorFromWindow==NULL
513 || gs_GetMonitorInfo==NULL || gs_EnumDisplayMonitors==NULL )
514 return;
515
516 DoRefreshMonitors();
517
518 // Also create a hidden window to listen for WM_SETTINGCHANGE that we
519 // receive when a monitor is added to or removed from the system as we must
520 // refresh our monitor handles information then.
521 m_hiddenHwnd = wxCreateHiddenWindow
522 (
523 &m_hiddenClass,
524 wxT("wxDisplayHiddenWindow"),
525 wxDisplayWndProc
526 );
527 }
528
529 wxDisplayFactoryMSW::~wxDisplayFactoryMSW()
530 {
531 if ( m_hiddenHwnd )
532 {
533 if ( !::DestroyWindow(m_hiddenHwnd) )
534 {
535 wxLogLastError(wxT("DestroyWindow(wxDisplayHiddenWindow)"));
536 }
537
538 if ( m_hiddenClass )
539 {
540 if ( !::UnregisterClass(m_hiddenClass, wxGetInstance()) )
541 {
542 wxLogLastError(wxT("UnregisterClass(wxDisplayHiddenWindow)"));
543 }
544 }
545 }
546
547 ms_factory = NULL;
548 }
549
550 void wxDisplayFactoryMSW::DoRefreshMonitors()
551 {
552 m_displays.Clear();
553
554 if ( !gs_EnumDisplayMonitors(NULL, NULL, MultimonEnumProc, (LPARAM)this) )
555 {
556 wxLogLastError(wxT("EnumDisplayMonitors"));
557 }
558 }
559
560 /* static */
561 BOOL CALLBACK
562 wxDisplayFactoryMSW::MultimonEnumProc(
563 HMONITOR hMonitor, // handle to display monitor
564 HDC WXUNUSED(hdcMonitor), // handle to monitor-appropriate device context
565 LPRECT WXUNUSED(lprcMonitor), // pointer to monitor intersection rectangle
566 LPARAM dwData) // data passed from EnumDisplayMonitors (this)
567 {
568 wxDisplayFactoryMSW *const self = (wxDisplayFactoryMSW *)dwData;
569
570 self->m_displays.Add(hMonitor);
571
572 // continue the enumeration
573 return TRUE;
574 }
575
576 wxDisplayImpl *wxDisplayFactoryMSW::CreateDisplay(unsigned n)
577 {
578 wxCHECK_MSG( n < m_displays.size(), NULL, wxT("An invalid index was passed to wxDisplay") );
579
580 return new wxDisplayMSW(n, m_displays[n]);
581 }
582
583 // helper for GetFromPoint() and GetFromWindow()
584 int wxDisplayFactoryMSW::FindDisplayFromHMONITOR(HMONITOR hmon) const
585 {
586 if ( hmon )
587 {
588 const size_t count = m_displays.size();
589 for ( size_t n = 0; n < count; n++ )
590 {
591 if ( hmon == m_displays[n] )
592 return n;
593 }
594 }
595
596 return wxNOT_FOUND;
597 }
598
599 int wxDisplayFactoryMSW::GetFromPoint(const wxPoint& pt)
600 {
601 POINT pt2;
602 pt2.x = pt.x;
603 pt2.y = pt.y;
604
605 return FindDisplayFromHMONITOR(gs_MonitorFromPoint(pt2,
606 MONITOR_DEFAULTTONULL));
607 }
608
609 int wxDisplayFactoryMSW::GetFromWindow(const wxWindow *window)
610 {
611 #ifdef __WXMSW__
612 return FindDisplayFromHMONITOR(gs_MonitorFromWindow(GetHwndOf(window),
613 MONITOR_DEFAULTTONULL));
614 #else
615 const wxSize halfsize = window->GetSize() / 2;
616 wxPoint pt = window->GetScreenPosition();
617 pt.x += halfsize.x;
618 pt.y += halfsize.y;
619 return GetFromPoint(pt);
620 #endif
621 }
622
623 #endif // wxUSE_DISPLAY
624
625 void wxClientDisplayRect(int *x, int *y, int *width, int *height)
626 {
627 #if defined(__WXMICROWIN__)
628 *x = 0; *y = 0;
629 wxDisplaySize(width, height);
630 #else
631 // Determine the desktop dimensions minus the taskbar and any other
632 // special decorations...
633 RECT r;
634
635 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
636 if (x) *x = r.left;
637 if (y) *y = r.top;
638 if (width) *width = r.right - r.left;
639 if (height) *height = r.bottom - r.top;
640 #endif
641 }