]>
Commit | Line | Data |
---|---|---|
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 |