VirtualBox

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

Last change on this file since 34079 was 33966, checked in by vboxsync, 14 years ago

VBoxTray: Cleaning up, refactoring, grouping window messages.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 15.4 KB
Line 
1/* $Id: VBoxSeamless.cpp 33966 2010-11-11 10:32:07Z vboxsync $ */
2/** @file
3 * VBoxSeamless - Seamless windows
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. 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#define _WIN32_WINNT 0x0500
18#include <windows.h>
19#include "VBoxTray.h"
20#include "VBoxHelpers.h"
21#include "VBoxSeamless.h"
22#include <VBoxHook.h>
23#include <VBoxDisplay.h>
24#include <VBox/VMMDev.h>
25#include <iprt/assert.h>
26#include <VBoxGuestInternal.h>
27
28typedef struct _VBOXSEAMLESSCONTEXT
29{
30 const VBOXSERVICEENV *pEnv;
31
32 HMODULE hModule;
33
34 BOOL (* pfnVBoxInstallHook)(HMODULE hDll);
35 BOOL (* pfnVBoxRemoveHook)();
36
37 PVBOXDISPIFESCAPE lpEscapeData;
38} VBOXSEAMLESSCONTEXT;
39
40typedef struct
41{
42 HDC hdc;
43 HRGN hrgn;
44#ifndef MMSEAMLESS
45 RECT rect;
46#endif
47} VBOX_ENUM_PARAM, *PVBOX_ENUM_PARAM;
48
49static VBOXSEAMLESSCONTEXT gCtx = {0};
50
51void VBoxLogString(HANDLE hDriver, char *pszStr);
52
53int VBoxSeamlessInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
54{
55 Log(("VBoxTray: VBoxSeamlessInit\n"));
56
57 *pfStartThread = false;
58 gCtx.pEnv = pEnv;
59
60 OSVERSIONINFO OSinfo;
61 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
62 GetVersionEx (&OSinfo);
63
64 int rc = VINF_SUCCESS;
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 Log(("VBoxTray: VBoxSeamlessInit: Windows NT 4.0 or older not supported!\n"));
71 rc = VERR_NOT_SUPPORTED;
72 }
73 else
74 {
75 /* Will fail if SetWinEventHook is not present (version < NT4 SP6 apparently) */
76 gCtx.hModule = LoadLibrary(VBOXHOOK_DLL_NAME);
77 if (gCtx.hModule)
78 {
79 *(uintptr_t *)&gCtx.pfnVBoxInstallHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxInstallHook");
80 *(uintptr_t *)&gCtx.pfnVBoxRemoveHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxRemoveHook");
81
82 /* Inform the host that we support the seamless window mode. */
83 rc = VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS, 0);
84 if (RT_SUCCESS(rc))
85 {
86 *pfStartThread = true;
87 *ppInstance = &gCtx;
88 }
89 else
90 Log(("VBoxTray: VBoxSeamlessInit: Failed to set seamless capability\n"));
91 }
92 else
93 {
94 rc = RTErrConvertFromWin32(GetLastError());
95 Log(("VBoxTray: VBoxSeamlessInit: LoadLibrary of \"%s\" failed with rc=%Rrc\n", VBOXHOOK_DLL_NAME, rc));
96 }
97 }
98
99 return rc;
100}
101
102
103void VBoxSeamlessDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
104{
105 Log(("VBoxTray: VBoxSeamlessDestroy\n"));
106
107 /* Inform the host that we no longer support the seamless window mode. */
108 int rc = VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS);
109 if (RT_FAILURE(rc))
110 Log(("VBoxTray: VBoxSeamlessDestroy: Failed to unset seamless capability, rc=%Rrc\n", rc));
111
112 if (gCtx.pfnVBoxRemoveHook)
113 gCtx.pfnVBoxRemoveHook();
114 if (gCtx.hModule)
115 FreeLibrary(gCtx.hModule);
116 gCtx.hModule = 0;
117 return;
118}
119
120void VBoxSeamlessInstallHook()
121{
122 if (gCtx.pfnVBoxInstallHook)
123 {
124 /* Check current visible region state */
125 VBoxSeamlessCheckWindows();
126
127 gCtx.pfnVBoxInstallHook(gCtx.hModule);
128 }
129}
130
131void VBoxSeamlessRemoveHook()
132{
133 if (gCtx.pfnVBoxRemoveHook)
134 gCtx.pfnVBoxRemoveHook();
135
136 if (gCtx.lpEscapeData)
137 {
138 free(gCtx.lpEscapeData);
139 gCtx.lpEscapeData = NULL;
140 }
141}
142
143BOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam)
144{
145 PVBOX_ENUM_PARAM lpParam = (PVBOX_ENUM_PARAM)lParam;
146 DWORD dwStyle, dwExStyle;
147 RECT rectWindow, rectVisible;
148
149 dwStyle = GetWindowLong(hwnd, GWL_STYLE);
150 dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
151 if ( !(dwStyle & WS_VISIBLE)
152 || (dwStyle & WS_CHILD))
153 return TRUE;
154
155 Log(("VBoxTray: VBoxEnumFunc %x\n", hwnd));
156 /* Only visible windows that are present on the desktop are interesting here */
157#ifndef MMSEAMLESS
158 if ( GetWindowRect(hwnd, &rectWindow)
159 && IntersectRect(&rectVisible, &lpParam->rect, &rectWindow))
160 {
161#else
162 if (GetWindowRect(hwnd, &rectWindow))
163 {
164 rectVisible = rectWindow;
165#endif
166 char szWindowText[256];
167 szWindowText[0] = 0;
168 GetWindowText(hwnd, szWindowText, sizeof(szWindowText));
169
170 /* Filter out Windows XP shadow windows */
171 /** @todo still shows inside the guest */
172 if ( szWindowText[0] == 0
173 && dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS)
174 && dwExStyle == (WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_TRANSPARENT|WS_EX_TOPMOST))
175 {
176 Log(("VBoxTray: Filter out shadow window style=%x exstyle=%x\n", dwStyle, dwExStyle));
177 return TRUE;
178 }
179
180 /** @todo will this suffice? The Program Manager window covers the whole screen */
181 if (strcmp(szWindowText, "Program Manager"))
182 {
183 Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
184 Log(("VBoxTray: title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
185
186 HRGN hrgn = CreateRectRgn(0,0,0,0);
187
188 int ret = GetWindowRgn(hwnd, hrgn);
189
190 if (ret == ERROR)
191 {
192 Log(("VBoxTray: GetWindowRgn failed with rc=%d\n", GetLastError()));
193 SetRectRgn(hrgn, rectVisible.left, rectVisible.top, rectVisible.right, rectVisible.bottom);
194 }
195 else
196 {
197 /* this region is relative to the window origin instead of the desktop origin */
198 OffsetRgn(hrgn, rectWindow.left, rectWindow.top);
199 }
200 if (lpParam->hrgn)
201 {
202 /* create a union of the current visible region and the visible rectangle of this window. */
203 CombineRgn(lpParam->hrgn, lpParam->hrgn, hrgn, RGN_OR);
204 DeleteObject(hrgn);
205 }
206 else
207 lpParam->hrgn = hrgn;
208 }
209 else
210 {
211 Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d) (ignored)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
212 Log(("VBoxTray: title=%s style=%x\n", szWindowText, dwStyle));
213 }
214 }
215 return TRUE; /* continue enumeration */
216}
217
218void VBoxSeamlessCheckWindows()
219{
220 VBOX_ENUM_PARAM param;
221
222 param.hdc = GetDC(HWND_DESKTOP);
223 param.hrgn = 0;
224
225#ifndef MMSEAMLESS
226 GetWindowRect(GetDesktopWindow(), &param.rect);
227 Log(("VBoxTray: VBoxRecheckVisibleWindows desktop=%x rect (%d,%d) (%d,%d)\n", GetDesktopWindow(), param.rect.left, param.rect.top, param.rect.right, param.rect.bottom));
228#endif
229 EnumWindows(VBoxEnumFunc, (LPARAM)&param);
230
231 if (param.hrgn)
232 {
233 DWORD cbSize;
234
235 cbSize = GetRegionData(param.hrgn, 0, NULL);
236 if (cbSize)
237 {
238 PVBOXDISPIFESCAPE lpEscapeData = (PVBOXDISPIFESCAPE)malloc(VBOXDISPIFESCAPE_SIZE(cbSize));
239 if (lpEscapeData)
240 {
241 lpEscapeData->escapeCode = VBOXESC_SETVISIBLEREGION;
242 LPRGNDATA lpRgnData = VBOXDISPIFESCAPE_DATA(lpEscapeData, RGNDATA);
243 memset(lpRgnData, 0, cbSize);
244 cbSize = GetRegionData(param.hrgn, cbSize, lpRgnData);
245 if (cbSize)
246 {
247#ifdef DEBUG
248 RECT *lpRect = (RECT *)&lpRgnData->Buffer[0];
249 Log(("VBoxTray: New visible region: \n"));
250
251 for (DWORD i=0;i<lpRgnData->rdh.nCount;i++)
252 {
253 Log(("VBoxTray: visible rect (%d,%d)(%d,%d)\n", lpRect[i].left, lpRect[i].top, lpRect[i].right, lpRect[i].bottom));
254 }
255#endif
256 LPRGNDATA lpCtxRgnData = VBOXDISPIFESCAPE_DATA(gCtx.lpEscapeData, RGNDATA);
257 if ( !gCtx.lpEscapeData
258 || (lpCtxRgnData->rdh.dwSize + lpCtxRgnData->rdh.nRgnSize != cbSize)
259 || memcmp(lpCtxRgnData, lpRgnData, cbSize))
260 {
261 /* send to display driver */
262 VBoxDispIfEscape(&gCtx.pEnv->dispIf, lpEscapeData, cbSize);
263
264 if (gCtx.lpEscapeData)
265 free(gCtx.lpEscapeData);
266 gCtx.lpEscapeData = lpEscapeData;
267 }
268 else
269 Log(("VBoxTray: Visible rectangles haven't changed; ignore\n"));
270 }
271 if (lpEscapeData != gCtx.lpEscapeData)
272 free(lpEscapeData);
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 Log(("VBoxTray: VBoxSeamlessThread: DeviceIOControl(CtlMask - or) succeeded\n"));
300 }
301 else
302 {
303 Log(("VBoxTray: 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 Log(("VBoxTray: VBoxSeamlessThread: DeviceIOControl succeeded\n"));
316
317 /* are we supposed to stop? */
318 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0)
319 break;
320
321 Log(("VBoxTray: VBoxSeamlessThread: checking event\n"));
322
323 /* did we get the right event? */
324 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST)
325 {
326 Log(("VBoxTray: 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 Log(("VBoxTray: VBoxSeamlessThread: mode change to %d\n", seamlessChangeRequest.mode));
344
345 switch(seamlessChangeRequest.mode)
346 {
347 case VMMDev_Seamless_Disabled:
348 if (fWasScreenSaverActive)
349 {
350 Log(("VBoxTray: Re-enabling the screensaver\n"));
351 ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0);
352 if (!ret)
353 Log(("VBoxTray: 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 Log(("VBoxTray: SystemParametersInfo SPI_GETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
362
363 if (fWasScreenSaverActive)
364 Log(("VBoxTray: Disabling the screensaver\n"));
365
366 ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
367 if (!ret)
368 Log(("VBoxTray: 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 Log(("VBoxTray: 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 Log(("VBoxTray: 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 Log(("VBoxTray: VBoxSeamlessThread: DeviceIOControl(CtlMask - not) succeeded\n"));
413 }
414 else
415 {
416 Log(("VBoxTray: VBoxSeamlessThread: DeviceIOControl(CtlMask) failed\n"));
417 }
418
419 Log(("VBoxTray: 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