]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/display.cpp
Fix pulsing of bitmaps in focused buttons under Windows 7.
[wxWidgets.git] / src / msw / display.cpp
... / ...
CommitLineData
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__
76static const wxChar displayDllName[] = wxT("coredll.dll");
77#else
78static const wxChar displayDllName[] = wxT("user32.dll");
79#endif
80
81// ----------------------------------------------------------------------------
82// typedefs for dynamically loaded Windows functions
83// ----------------------------------------------------------------------------
84
85typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName,
86 LPDEVMODE lpDevMode,
87 HWND hwnd,
88 DWORD dwFlags,
89 LPVOID lParam);
90
91typedef BOOL (WINAPI *EnumDisplayMonitors_t)(HDC,LPCRECT,MONITORENUMPROC,LPARAM);
92typedef HMONITOR (WINAPI *MonitorFromPoint_t)(POINT,DWORD);
93typedef HMONITOR (WINAPI *MonitorFromWindow_t)(HWND,DWORD);
94typedef BOOL (WINAPI *GetMonitorInfo_t)(HMONITOR,LPMONITORINFO);
95
96#ifndef __WXWINCE__
97// emulation of ChangeDisplaySettingsEx() for Win95
98LONG 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
113class wxDisplayMSW : public wxDisplayImpl
114{
115public:
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
131protected:
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
150private:
151 wxDECLARE_NO_COPY_CLASS(wxDisplayMSW);
152};
153
154
155// ----------------------------------------------------------------------------
156// wxDisplayFactoryMSW declaration
157// ----------------------------------------------------------------------------
158
159WX_DEFINE_ARRAY(HMONITOR, wxMonitorHandleArray);
160
161// functions dynamically bound by wxDisplayFactoryMSW ctor.
162static MonitorFromPoint_t gs_MonitorFromPoint = NULL;
163static MonitorFromWindow_t gs_MonitorFromWindow = NULL;
164static GetMonitorInfo_t gs_GetMonitorInfo = NULL;
165static EnumDisplayMonitors_t gs_EnumDisplayMonitors = NULL;
166
167class wxDisplayFactoryMSW : public wxDisplayFactory
168{
169public:
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
190private:
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
223wxDisplayFactoryMSW* 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
247bool 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
258wxRect 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
269wxRect 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
280wxString 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
291bool 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
301wxVideoMode 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
329wxArrayVideoModes 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
359bool 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
473LRESULT APIENTRY
474wxDisplayWndProc(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
486wxDisplayFactoryMSW::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
529wxDisplayFactoryMSW::~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
550void 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 */
561BOOL CALLBACK
562wxDisplayFactoryMSW::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
576wxDisplayImpl *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()
584int 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
599int 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
609int 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
625void 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}