blob: 03772e97ea421d92db2bfb6079e6de307edce083 [file] [log] [blame]
Constantin Kaplinsky0a1eca12006-04-16 07:28:14 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19#include <rfb_win32/DynamicFn.h>
20#include <rfb_win32/MonitorInfo.h>
21#include <rfb_win32/Win32Util.h>
22#include <rdr/Exception.h>
23#include <rfb/LogWriter.h>
24
25using namespace rfb;
26using namespace win32;
27
28static LogWriter vlog("MonitorInfo");
29
30
31// If we are building in multi-monitor support (i.e. the headers support it)
32// then do dynamic imports of the required system calls, and provide any
33// other code that wouldn't otherwise compile.
34#ifdef RFB_HAVE_MONITORINFO
35#include <tchar.h>
36typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
37static rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
38typedef HMONITOR (WINAPI *_MonitorFromRect_proto)(LPCRECT,DWORD);
39static rfb::win32::DynamicFn<_MonitorFromRect_proto> _MonitorFromRect(_T("user32.dll"), "MonitorFromRect");
40typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
41static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
42typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
43static rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
44static void fillMonitorInfo(HMONITOR monitor, MonitorInfo* mi) {
45 vlog.debug("monitor=%lx", monitor);
46 if (!_GetMonitorInfo.isValid())
47 throw rdr::Exception("no GetMonitorInfo");
48 memset(mi, 0, sizeof(MONITORINFOEXA));
49 mi->cbSize = sizeof(MONITORINFOEXA);
50 if (!(*_GetMonitorInfo)(monitor, mi))
51 throw rdr::SystemException("failed to GetMonitorInfo", GetLastError());
52 vlog.debug("monitor is %d,%d-%d,%d", mi->rcMonitor.left, mi->rcMonitor.top, mi->rcMonitor.right, mi->rcMonitor.bottom);
53 vlog.debug("work area is %d,%d-%d,%d", mi->rcWork.left, mi->rcWork.top, mi->rcWork.right, mi->rcWork.bottom);
54 vlog.debug("device is \"%s\"", mi->szDevice);
55}
56#else
57#pragma message(" NOTE: Not building Multi-Monitor support.")
58#endif
59
60
61MonitorInfo::MonitorInfo(HWND window) {
62 cbSize = sizeof(MonitorInfo);
63 szDevice[0] = 0;
64
65#ifdef RFB_HAVE_MONITORINFO
66 try {
67 if (_MonitorFromWindow.isValid()) {
68 HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
69 if (!monitor)
70 throw rdr::SystemException("failed to get monitor", GetLastError());
71 fillMonitorInfo(monitor, this);
72 return;
73 }
74 } catch (rdr::Exception& e) {
75 vlog.error(e.str());
76 }
77#endif
78
79 // Legacy fallbacks - just return the desktop settings
80 vlog.debug("using legacy fall-backs");
81 HWND desktop = GetDesktopWindow();
82 GetWindowRect(desktop, &rcMonitor);
83 SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
84 dwFlags = 0;
85}
86
87MonitorInfo::MonitorInfo(const RECT& r) {
88 cbSize = sizeof(MonitorInfo);
89 szDevice[0] = 0;
90
91#ifdef RFB_HAVE_MONITORINFO
92 try {
93 if (_MonitorFromRect.isValid()) {
94 HMONITOR monitor = (*_MonitorFromRect)(&r, MONITOR_DEFAULTTONEAREST);
95 if (!monitor)
96 throw rdr::SystemException("failed to get monitor", GetLastError());
97 fillMonitorInfo(monitor, this);
98 return;
99 }
100 } catch (rdr::Exception& e) {
101 vlog.error(e.str());
102 }
103#endif
104
105 // Legacy fallbacks - just return the desktop settings
106 vlog.debug("using legacy fall-backs");
107 HWND desktop = GetDesktopWindow();
108 GetWindowRect(desktop, &rcMonitor);
109 SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
110 dwFlags = 0;
111}
112
113
114#ifdef RFB_HAVE_MONITORINFO
115
116struct monitorByNameData {
117 MonitorInfo* info;
118 const char* monitorName;
119};
120
121static BOOL CALLBACK monitorByNameEnumProc(HMONITOR monitor,
122 HDC dc,
123 LPRECT pos,
124 LPARAM d) {
125 monitorByNameData* data = (monitorByNameData*)d;
126 memset(data->info, 0, sizeof(MONITORINFOEXA));
127 data->info->cbSize = sizeof(MONITORINFOEXA);
128 if ((*_GetMonitorInfo)(monitor, data->info)) {
129 if (stricmp(data->monitorName, data->info->szDevice) == 0)
130 return FALSE;
131 }
132
133 return TRUE;
134}
135
136#endif
137
138MonitorInfo::MonitorInfo(const char* devName) {
139#ifdef RFB_HAVE_MONITORINFO
140 if (!_EnumDisplayMonitors.isValid()) {
141 vlog.debug("EnumDisplayMonitors not found");
142 } else {
143 monitorByNameData data;
144 data.info = this;
145 data.monitorName = devName;
146
147 (*_EnumDisplayMonitors)(0, 0, &monitorByNameEnumProc, (LPARAM)&data);
148 if (stricmp(data.monitorName, szDevice) == 0)
149 return;
150 }
151#endif
152 // If multi-monitor is not built, or not supported by the OS,
153 // or if the named monitor is not found, revert to the primary monitor.
154 vlog.debug("reverting to primary monitor");
155 cbSize = sizeof(MonitorInfo);
156 szDevice[0] = 0;
157
158 HWND desktop = GetDesktopWindow();
159 GetWindowRect(desktop, &rcMonitor);
160 SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
161 dwFlags = 0;
162}
163
164void MonitorInfo::moveTo(HWND handle) {
165 vlog.debug("moveTo monitor=%s", szDevice);
166
167#ifdef RFB_HAVE_MONITORINFO
168 MonitorInfo mi(handle);
169 if (strcmp(szDevice, mi.szDevice) != 0) {
170 centerWindow(handle, rcWork);
171 clipTo(handle);
172 }
173#endif
174}
175
176void MonitorInfo::clipTo(RECT* r) {
177 vlog.debug("clipTo monitor=%s", szDevice);
178
179 if (r->top < rcWork.top) {
180 r->bottom += rcWork.top - r->top; r->top = rcWork.top;
181 }
182 if (r->left < rcWork.left) {
183 r->right += rcWork.left - r->left; r->left = rcWork.left;
184 }
185 if (r->bottom > rcWork.bottom) {
186 r->top += rcWork.bottom - r->bottom; r->bottom = rcWork.bottom;
187 }
188 if (r->right > rcWork.right) {
189 r->left += rcWork.right - r->right; r->right = rcWork.right;
190 }
191 r->left = max(r->left, rcWork.left);
192 r->right = min(r->right, rcWork.right);
193 r->top = max(r->top, rcWork.top);
194 r->bottom = min(r->bottom, rcWork.bottom);
195}
196
197void MonitorInfo::clipTo(HWND handle) {
198 RECT r;
199 GetWindowRect(handle, &r);
200 clipTo(&r);
201 SetWindowPos(handle, 0, r.left, r.top, r.right-r.left, r.bottom-r.top,
202 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
203}
204
205