Constantin Kaplinsky | 0a1eca1 | 2006-04-16 07:28:14 +0000 | [diff] [blame^] | 1 | /* 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 | |
| 25 | using namespace rfb; |
| 26 | using namespace win32; |
| 27 | |
| 28 | static 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> |
| 36 | typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD); |
| 37 | static rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow"); |
| 38 | typedef HMONITOR (WINAPI *_MonitorFromRect_proto)(LPCRECT,DWORD); |
| 39 | static rfb::win32::DynamicFn<_MonitorFromRect_proto> _MonitorFromRect(_T("user32.dll"), "MonitorFromRect"); |
| 40 | typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO); |
| 41 | static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA"); |
| 42 | typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM); |
| 43 | static rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors"); |
| 44 | static 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 | |
| 61 | MonitorInfo::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 | |
| 87 | MonitorInfo::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 | |
| 116 | struct monitorByNameData { |
| 117 | MonitorInfo* info; |
| 118 | const char* monitorName; |
| 119 | }; |
| 120 | |
| 121 | static 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 | |
| 138 | MonitorInfo::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 | |
| 164 | void 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 | |
| 176 | void 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 | |
| 197 | void 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 | |