VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.cpp@ 10552

Last change on this file since 10552 was 10552, checked in by vboxsync, 16 years ago

More IOCTLs.

  • Property svn:eol-style set to native
File size: 15.6 KB
Line 
1/** @file
2 *
3 * VBoxSeamless - Seamless windows
4 *
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22#define _WIN32_WINNT 0x0500
23#include <windows.h>
24#include "VBoxTray.h"
25#include "VBoxSeamless.h"
26#include <VBoxHook.h>
27#include <VBoxDisplay.h>
28#include <VBox/VBoxDev.h>
29#include <iprt/assert.h>
30#include "helpers.h"
31
32typedef struct _VBOXSEAMLESSCONTEXT
33{
34 const VBOXSERVICEENV *pEnv;
35
36 HMODULE hModule;
37
38 BOOL (* pfnVBoxInstallHook)(HMODULE hDll);
39 BOOL (* pfnVBoxRemoveHook)();
40
41 LPRGNDATA lpRgnData;
42} VBOXSEAMLESSCONTEXT;
43
44typedef struct
45{
46 HDC hdc;
47 HRGN hrgn;
48 RECT rect;
49} VBOX_ENUM_PARAM, *PVBOX_ENUM_PARAM;
50
51static VBOXSEAMLESSCONTEXT gCtx = {0};
52
53void VBoxLogString(HANDLE hDriver, char *pszStr);
54
55int VBoxSeamlessInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
56{
57 dprintf(("VBoxSeamlessInit\n"));
58
59 *pfStartThread = false;
60 gCtx.pEnv = pEnv;
61
62 OSVERSIONINFO OSinfo;
63 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
64 GetVersionEx (&OSinfo);
65
66 /* We have to jump out here when using NT4, otherwise it complains about
67 a missing API function "UnhookWinEvent" used by the dynamically loaded VBoxHook.dll below */
68 if (OSinfo.dwMajorVersion <= 4) /* Windows NT 4.0 or older */
69 {
70 dprintf(("VBoxSeamlessInit: Windows NT 4.0 or older not supported!"));
71 return VERR_NOT_SUPPORTED;
72 }
73
74 /* Will fail if SetWinEventHook is not present (version < NT4 SP6 apparently) */
75 gCtx.hModule = LoadLibrary(VBOXHOOK_DLL_NAME);
76 if (gCtx.hModule)
77 {
78 *(uintptr_t *)&gCtx.pfnVBoxInstallHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxInstallHook");
79 *(uintptr_t *)&gCtx.pfnVBoxRemoveHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxRemoveHook");
80
81 /* inform the host that we support the seamless window mode */
82 VMMDevReqGuestCapabilities vmmreqGuestCaps = {0};
83 vmmdevInitRequest((VMMDevRequestHeader*)&vmmreqGuestCaps, VMMDevReq_ReportGuestCapabilities);
84 vmmreqGuestCaps.caps = VMMDEV_GUEST_SUPPORTS_SEAMLESS;
85
86 DWORD cbReturned;
87 if (!DeviceIoControl(pEnv->hDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(vmmreqGuestCaps)), &vmmreqGuestCaps, sizeof(vmmreqGuestCaps),
88 &vmmreqGuestCaps, sizeof(vmmreqGuestCaps), &cbReturned, NULL))
89 {
90 dprintf(("VBoxSeamlessInit: VMMDevReq_ReportGuestCapabilities: error doing IOCTL, last error: %d\n", GetLastError()));
91 return VERR_INVALID_PARAMETER;
92 }
93
94 *pfStartThread = true;
95 *ppInstance = &gCtx;
96 return VINF_SUCCESS;
97 }
98 else
99 {
100 dprintf(("VBoxSeamlessInit: LoadLibrary failed with %d\n", GetLastError()));
101 return VERR_INVALID_PARAMETER;
102 }
103
104 return VINF_SUCCESS;
105}
106
107
108void VBoxSeamlessDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
109{
110 dprintf(("VBoxSeamlessDestroy\n"));
111 /* inform the host that we no longer support the seamless window mode */
112 VMMDevReqGuestCapabilities vmmreqGuestCaps = {0};
113 vmmdevInitRequest((VMMDevRequestHeader*)&vmmreqGuestCaps, VMMDevReq_ReportGuestCapabilities);
114 vmmreqGuestCaps.caps = 0;
115
116 DWORD cbReturned;
117 if (!DeviceIoControl(pEnv->hDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(vmmreqGuestCaps)), &vmmreqGuestCaps, sizeof(vmmreqGuestCaps),
118 &vmmreqGuestCaps, sizeof(vmmreqGuestCaps), &cbReturned, NULL))
119 {
120 dprintf(("VMMDevReq_ReportGuestCapabilities: error doing IOCTL, last error: %d\n", GetLastError()));
121 }
122
123 if (gCtx.pfnVBoxRemoveHook)
124 gCtx.pfnVBoxRemoveHook();
125 if (gCtx.hModule)
126 FreeLibrary(gCtx.hModule);
127 gCtx.hModule = 0;
128 return;
129}
130
131void VBoxSeamlessInstallHook()
132{
133 if (gCtx.pfnVBoxInstallHook)
134 {
135 /* Check current visible region state */
136 VBoxSeamlessCheckWindows();
137
138 gCtx.pfnVBoxInstallHook(gCtx.hModule);
139 }
140}
141
142void VBoxSeamlessRemoveHook()
143{
144 if (gCtx.pfnVBoxRemoveHook)
145 gCtx.pfnVBoxRemoveHook();
146
147 if (gCtx.lpRgnData)
148 {
149 free(gCtx.lpRgnData);
150 gCtx.lpRgnData = NULL;
151 }
152}
153
154BOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam)
155{
156 PVBOX_ENUM_PARAM lpParam = (PVBOX_ENUM_PARAM)lParam;
157 DWORD dwStyle, dwExStyle;
158 RECT rectWindow, rectVisible;
159
160 dwStyle = GetWindowLong(hwnd, GWL_STYLE);
161 dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
162 if ( !(dwStyle & WS_VISIBLE)
163 || (dwStyle & WS_CHILD))
164 return TRUE;
165
166 dprintf(("VBoxEnumFunc %x\n", hwnd));
167 /* Only visible windows that are present on the desktop are interesting here */
168 if ( GetWindowRect(hwnd, &rectWindow)
169 && IntersectRect(&rectVisible, &lpParam->rect, &rectWindow))
170 {
171 char szWindowText[256];
172 szWindowText[0] = 0;
173 GetWindowText(hwnd, szWindowText, sizeof(szWindowText));
174
175 /* Filter out Windows XP shadow windows */
176 /** @todo still shows inside the guest */
177 if ( szWindowText[0] == 0
178 && dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS)
179 && dwExStyle == (WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_TRANSPARENT|WS_EX_TOPMOST))
180 {
181 dprintf(("Filter out shadow window style=%x exstyle=%x\n", dwStyle, dwExStyle));
182 return TRUE;
183 }
184
185 /** @todo will this suffice? The Program Manager window covers the whole screen */
186 if (strcmp(szWindowText, "Program Manager"))
187 {
188 dprintf(("Enum hwnd=%x rect (%d,%d) (%d,%d)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
189 dprintf(("title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
190
191 HRGN hrgn = CreateRectRgn(0,0,0,0);
192
193 int ret = GetWindowRgn(hwnd, hrgn);
194
195 if (ret == ERROR)
196 {
197 dprintf(("GetWindowRgn failed with rc=%d\n", GetLastError()));
198 SetRectRgn(hrgn, rectVisible.left, rectVisible.top, rectVisible.right, rectVisible.bottom);
199 }
200 else
201 {
202 /* this region is relative to the window origin instead of the desktop origin */
203 OffsetRgn(hrgn, rectWindow.left, rectWindow.top);
204 }
205 if (lpParam->hrgn)
206 {
207 /* create a union of the current visible region and the visible rectangle of this window. */
208 CombineRgn(lpParam->hrgn, lpParam->hrgn, hrgn, RGN_OR);
209 DeleteObject(hrgn);
210 }
211 else
212 lpParam->hrgn = hrgn;
213 }
214 else
215 {
216 dprintf(("Enum hwnd=%x rect (%d,%d) (%d,%d) (ignored)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
217 dprintf(("title=%s style=%x\n", szWindowText, dwStyle));
218 }
219 }
220 return TRUE; /* continue enumeration */
221}
222
223void VBoxSeamlessCheckWindows()
224{
225 VBOX_ENUM_PARAM param;
226
227 param.hdc = GetDC(HWND_DESKTOP);
228 param.hrgn = 0;
229
230 GetWindowRect(GetDesktopWindow(), &param.rect);
231 dprintf(("VBoxRecheckVisibleWindows desktop=%x rect (%d,%d) (%d,%d)\n", GetDesktopWindow(), param.rect.left, param.rect.top, param.rect.right, param.rect.bottom));
232 EnumWindows(VBoxEnumFunc, (LPARAM)&param);
233
234 if (param.hrgn)
235 {
236 DWORD cbSize;
237
238 cbSize = GetRegionData(param.hrgn, 0, NULL);
239 if (cbSize)
240 {
241 LPRGNDATA lpRgnData = (LPRGNDATA)malloc(cbSize);
242 memset(lpRgnData, 0, cbSize);
243 if (lpRgnData)
244 {
245 cbSize = GetRegionData(param.hrgn, cbSize, lpRgnData);
246 if (cbSize)
247 {
248#ifdef DEBUG
249 RECT *lpRect = (RECT *)&lpRgnData->Buffer[0];
250 dprintf(("New visible region: \n"));
251
252 for (DWORD i=0;i<lpRgnData->rdh.nCount;i++)
253 {
254 dprintf(("visible rect (%d,%d)(%d,%d)\n", lpRect[i].left, lpRect[i].top, lpRect[i].right, lpRect[i].bottom));
255 }
256#endif
257 if ( !gCtx.lpRgnData
258 || (gCtx.lpRgnData->rdh.dwSize + gCtx.lpRgnData->rdh.nRgnSize != cbSize)
259 || memcmp(gCtx.lpRgnData, lpRgnData, cbSize))
260 {
261 /* send to display driver */
262 ExtEscape(param.hdc, VBOXESC_SETVISIBLEREGION, cbSize, (LPCSTR)lpRgnData, 0, NULL);
263
264 if (gCtx.lpRgnData)
265 free(gCtx.lpRgnData);
266 gCtx.lpRgnData = lpRgnData;
267 }
268 else
269 dprintf(("Visible rectangles haven't changed; ignore\n"));
270 }
271 if (lpRgnData != gCtx.lpRgnData)
272 free(lpRgnData);
273 }
274 }
275
276 DeleteObject(param.hrgn);
277 }
278
279 ReleaseDC(HWND_DESKTOP, param.hdc);
280}
281
282/**
283 * Thread function to wait for and process seamless mode change
284 * requests
285 */
286unsigned __stdcall VBoxSeamlessThread(void *pInstance)
287{
288 VBOXSEAMLESSCONTEXT *pCtx = (VBOXSEAMLESSCONTEXT *)pInstance;
289 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
290 bool fTerminate = false;
291 VBoxGuestFilterMaskInfo maskInfo;
292 DWORD cbReturned;
293 BOOL fWasScreenSaverActive = FALSE, ret;
294
295 maskInfo.u32OrMask = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
296 maskInfo.u32NotMask = 0;
297 if (DeviceIoControl (gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
298 {
299 dprintf(("VBoxSeamlessThread: DeviceIOControl(CtlMask - or) succeeded\n"));
300 }
301 else
302 {
303 dprintf(("VBoxSeamlessThread: DeviceIOControl(CtlMask) failed, SeamlessChangeThread exited\n"));
304 return 0;
305 }
306
307 do
308 {
309 /* wait for a seamless change event */
310 VBoxGuestWaitEventInfo waitEvent;
311 waitEvent.u32TimeoutIn = 5000;
312 waitEvent.u32EventMaskIn = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
313 if (DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
314 {
315 dprintf(("VBoxSeamlessThread: DeviceIOControl succeded\n"));
316
317 /* are we supposed to stop? */
318 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0)
319 break;
320
321 dprintf(("VBoxSeamlessThread: checking event\n"));
322
323 /* did we get the right event? */
324 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST)
325 {
326 dprintf(("VBoxTray: going to get seamless change information.\n"));
327
328 /* We got at least one event. Read the requested resolution
329 * and try to set it until success. New events will not be seen
330 * but a new resolution will be read in this poll loop.
331 */
332 for (;;)
333 {
334 /* get the seamless change request */
335 VMMDevSeamlessChangeRequest seamlessChangeRequest = {0};
336 vmmdevInitRequest((VMMDevRequestHeader*)&seamlessChangeRequest, VMMDevReq_GetSeamlessChangeRequest);
337 seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
338
339 BOOL fSeamlessChangeQueried = DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(seamlessChangeRequest)), &seamlessChangeRequest, sizeof(seamlessChangeRequest),
340 &seamlessChangeRequest, sizeof(seamlessChangeRequest), &cbReturned, NULL);
341 if (fSeamlessChangeQueried)
342 {
343 dprintf(("VBoxSeamlessThread: mode change to %d\n", seamlessChangeRequest.mode));
344
345 switch(seamlessChangeRequest.mode)
346 {
347 case VMMDev_Seamless_Disabled:
348 if (fWasScreenSaverActive)
349 {
350 dprintf(("Re-enabling the screensaver\n"));
351 ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0);
352 if (!ret)
353 dprintf(("SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
354 }
355 PostMessage(gToolWindow, WM_VBOX_REMOVE_SEAMLESS_HOOK, 0, 0);
356 break;
357
358 case VMMDev_Seamless_Visible_Region:
359 ret = SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fWasScreenSaverActive, 0);
360 if (!ret)
361 dprintf(("SystemParametersInfo SPI_GETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
362
363 if (fWasScreenSaverActive)
364 dprintf(("Disabling the screensaver\n"));
365
366 ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
367 if (!ret)
368 dprintf(("SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
369 PostMessage(gToolWindow, WM_VBOX_INSTALL_SEAMLESS_HOOK, 0, 0);
370 break;
371
372 case VMMDev_Seamless_Host_Window:
373 break;
374
375 default:
376 AssertFailed();
377 break;
378 }
379 break;
380 }
381 else
382 {
383 dprintf(("VBoxSeamlessThread: error from DeviceIoControl VBOXGUEST_IOCTL_VMMREQUEST\n"));
384 }
385 /* sleep a bit to not eat too much CPU while retrying */
386 /* are we supposed to stop? */
387 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 50) == WAIT_OBJECT_0)
388 {
389 fTerminate = true;
390 break;
391 }
392 }
393 }
394 }
395 else
396 {
397 dprintf(("VBoxTray: error 0 from DeviceIoControl VBOXGUEST_IOCTL_WAITEVENT\n"));
398 /* sleep a bit to not eat too much CPU in case the above call always fails */
399 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 10) == WAIT_OBJECT_0)
400 {
401 fTerminate = true;
402 break;
403 }
404 }
405 }
406 while (!fTerminate);
407
408 maskInfo.u32OrMask = 0;
409 maskInfo.u32NotMask = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
410 if (DeviceIoControl (gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
411 {
412 dprintf(("VBoxSeamlessThread: DeviceIOControl(CtlMask - not) succeeded\n"));
413 }
414 else
415 {
416 dprintf(("VBoxSeamlessThread: DeviceIOControl(CtlMask) failed\n"));
417 }
418
419 dprintf(("VBoxSeamlessThread: finished seamless change request thread\n"));
420 return 0;
421}
422
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette