]>
Commit | Line | Data |
---|---|---|
a536e411 JS |
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 | ||
8585e2b5 VZ |
37 | #include "wx/dynload.h" |
38 | ||
a536e411 JS |
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 | |
a536e411 | 43 | #define COMPILE_MULTIMON_STUBS |
8585e2b5 VZ |
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) | |
a536e411 JS |
54 | #endif |
55 | ||
8585e2b5 | 56 | #include <multimon.h> |
a536e411 | 57 | |
8585e2b5 VZ |
58 | #ifdef _MSC_VER |
59 | #pragma warning(default:4706) | |
60 | #endif | |
a536e411 | 61 | |
8585e2b5 VZ |
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 | |
a536e411 JS |
68 | |
69 | // ---------------------------------------------------------------------------- | |
8585e2b5 | 70 | // typedefs |
a536e411 JS |
71 | // ---------------------------------------------------------------------------- |
72 | ||
8585e2b5 VZ |
73 | typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName, |
74 | LPDEVMODE lpDevMode, | |
75 | HWND hwnd, | |
76 | DWORD dwFlags, | |
77 | LPVOID lParam); | |
a536e411 | 78 | |
8585e2b5 VZ |
79 | // ---------------------------------------------------------------------------- |
80 | // private classes | |
81 | // ---------------------------------------------------------------------------- | |
a536e411 | 82 | |
8585e2b5 | 83 | class wxDisplayInfo |
a536e411 JS |
84 | { |
85 | public: | |
8585e2b5 VZ |
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 | |
a536e411 | 90 | wxRect m_rect; |
8585e2b5 VZ |
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; } | |
a536e411 JS |
97 | }; |
98 | ||
8585e2b5 VZ |
99 | WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray); |
100 | #include "wx/arrimpl.cpp" | |
101 | WX_DEFINE_OBJARRAY(wxDisplayInfoArray); | |
a536e411 | 102 | |
8585e2b5 VZ |
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) | |
a536e411 JS |
119 | |
120 | // =========================================================================== | |
121 | // implementation | |
122 | // =========================================================================== | |
123 | ||
8585e2b5 VZ |
124 | // ---------------------------------------------------------------------------- |
125 | // local functions | |
126 | // ---------------------------------------------------------------------------- | |
127 | ||
128 | static BOOL CALLBACK wxmswMonitorEnumProc ( | |
a536e411 | 129 | HMONITOR hMonitor, // handle to display monitor |
8585e2b5 | 130 | HDC hdcMonitor, // handle to monitor-appropriate device context (NULL) |
a536e411 | 131 | LPRECT lprcMonitor, // pointer to monitor intersection rectangle |
8585e2b5 | 132 | LPARAM dwData // data passed from EnumDisplayMonitors (unused) |
a536e411 JS |
133 | ) |
134 | { | |
8585e2b5 VZ |
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() | |
a536e411 | 139 | info->m_hmon = hMonitor; |
8585e2b5 VZ |
140 | |
141 | // we also store the display geometry | |
a536e411 JS |
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 ); | |
8585e2b5 | 146 | |
a536e411 | 147 | // now add this monitor to the array |
8585e2b5 | 148 | g_displays->Add(info); |
a536e411 | 149 | |
8585e2b5 VZ |
150 | // continue the enumeration |
151 | return TRUE; | |
a536e411 JS |
152 | } |
153 | ||
8585e2b5 VZ |
154 | // this function must be called before accessing g_displays array as it |
155 | // creates and initializes it | |
156 | static void InitDisplays() | |
a536e411 | 157 | { |
8585e2b5 VZ |
158 | if ( g_displays ) |
159 | return; | |
a536e411 | 160 | |
8585e2b5 | 161 | g_displays = new wxDisplayInfoArray(); |
a536e411 | 162 | |
8585e2b5 VZ |
163 | // enumerate all displays |
164 | if ( !::EnumDisplayMonitors(NULL, NULL, wxmswMonitorEnumProc, 0) ) | |
a536e411 | 165 | { |
8585e2b5 VZ |
166 | wxLogLastError(wxT("EnumDisplayMonitors")); |
167 | ||
168 | // TODO: still create at least one (valid) entry in g_displays for the | |
169 | // primary display! | |
a536e411 | 170 | } |
8585e2b5 | 171 | } |
a536e411 | 172 | |
8585e2b5 VZ |
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 | } | |
a536e411 | 183 | |
8585e2b5 VZ |
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 | } | |
a536e411 | 193 | |
8585e2b5 VZ |
194 | // ---------------------------------------------------------------------------- |
195 | // wxDisplayModule | |
196 | // ---------------------------------------------------------------------------- | |
a536e411 | 197 | |
8585e2b5 VZ |
198 | void wxDisplayModule::OnExit() |
199 | { | |
200 | delete g_displays; | |
201 | } | |
a536e411 | 202 | |
8585e2b5 VZ |
203 | // --------------------------------------------------------------------------- |
204 | // wxDisplay | |
205 | // --------------------------------------------------------------------------- | |
a536e411 | 206 | |
8585e2b5 VZ |
207 | // helper of GetFromPoint() and GetFromWindow() |
208 | static int DisplayFromHMONITOR(HMONITOR hmon) | |
209 | { | |
210 | if ( hmon ) | |
211 | { | |
212 | const size_t count = wxDisplay::GetCount(); | |
a536e411 | 213 | |
8585e2b5 | 214 | for ( size_t n = 0; n < count; n++ ) |
a536e411 | 215 | { |
8585e2b5 VZ |
216 | if ( hmon == (*g_displays)[n].m_hmon ) |
217 | return n; | |
a536e411 | 218 | } |
a536e411 | 219 | } |
a536e411 | 220 | |
8585e2b5 | 221 | return wxNOT_FOUND; |
a536e411 JS |
222 | } |
223 | ||
8585e2b5 | 224 | /* static */ |
a536e411 JS |
225 | size_t wxDisplayBase::GetCount() |
226 | { | |
8585e2b5 VZ |
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(); | |
a536e411 JS |
235 | } |
236 | ||
8585e2b5 | 237 | /* static */ |
a536e411 JS |
238 | int wxDisplayBase::GetFromPoint ( const wxPoint& pt ) |
239 | { | |
240 | POINT pt2; | |
241 | pt2.x = pt.x; | |
242 | pt2.y = pt.y; | |
243 | ||
8585e2b5 VZ |
244 | return DisplayFromHMONITOR(::MonitorFromPoint(pt2, MONITOR_DEFAULTTONULL)); |
245 | } | |
a536e411 | 246 | |
8585e2b5 VZ |
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() ) | |
a536e411 | 273 | { |
8585e2b5 VZ |
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 | } | |
a536e411 JS |
286 | } |
287 | ||
8585e2b5 | 288 | return dpyInfo.m_devName; |
a536e411 JS |
289 | } |
290 | ||
8585e2b5 | 291 | wxString wxDisplay::GetNameForEnumSettings() const |
a536e411 | 292 | { |
8585e2b5 VZ |
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; | |
a536e411 JS |
305 | } |
306 | ||
8585e2b5 | 307 | wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const |
a536e411 | 308 | { |
8585e2b5 VZ |
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; | |
a536e411 JS |
328 | } |
329 | ||
8585e2b5 | 330 | wxVideoMode wxDisplay::GetCurrentMode() const |
a536e411 | 331 | { |
8585e2b5 VZ |
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; | |
a536e411 JS |
349 | } |
350 | ||
8585e2b5 | 351 | bool wxDisplay::ChangeMode(const wxVideoMode& mode) |
a536e411 | 352 | { |
8585e2b5 VZ |
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; | |
a536e411 JS |
439 | } |
440 | ||
8585e2b5 VZ |
441 | #endif // wxUSE_DISPLAY |
442 |