VirtualBox

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

Last change on this file since 45137 was 45137, checked in by vboxsync, 12 years ago

VBoxTray: fix visible regions for Aero

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

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