VirtualBox

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

Last change on this file since 60784 was 58307, checked in by vboxsync, 9 years ago

Additions/WINNT/VBoxGuestInternal.h: Removed empty misnamed file. VBoxGuestInternal.h is the internal header for Additions/common/VBoxGuest, not for the WINNT additions. Very confusing and pointless.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.8 KB
Line 
1/* $Id: VBoxSeamless.cpp 58307 2015-10-18 23:47:59Z vboxsync $ */
2/** @file
3 * VBoxSeamless - Seamless windows
4 */
5
6/*
7 * Copyright (C) 2006-2015 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
18#define _WIN32_WINNT 0x0500
19#include <Windows.h>
20
21#include <iprt/assert.h>
22#include <iprt/ldr.h>
23
24#include <VBoxDisplay.h>
25#include <VBoxHook.h>
26
27#ifdef DEBUG
28# define LOG_ENABLED
29# define LOG_GROUP LOG_GROUP_DEFAULT
30#endif
31#include <VBox/log.h>
32#include <VBox/VMMDev.h>
33
34#include "VBoxTray.h"
35#include "VBoxHelpers.h"
36#include "VBoxSeamless.h"
37
38
39
40typedef struct _VBOXSEAMLESSCONTEXT
41{
42 const VBOXSERVICEENV *pEnv;
43
44 RTLDRMOD hModHook;
45
46 BOOL (* pfnVBoxHookInstallWindowTracker)(HMODULE hDll);
47 BOOL (* pfnVBoxHookRemoveWindowTracker)();
48
49 PVBOXDISPIFESCAPE lpEscapeData;
50} VBOXSEAMLESSCONTEXT, *PVBOXSEAMLESSCONTEXT;
51
52typedef struct
53{
54 HDC hdc;
55 HRGN hrgn;
56} VBOX_ENUM_PARAM, *PVBOX_ENUM_PARAM;
57
58static VBOXSEAMLESSCONTEXT g_Ctx = { 0 };
59
60void VBoxLogString(HANDLE hDriver, char *pszStr);
61
62DECLCALLBACK(int) VBoxSeamlessInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
63{
64 LogFlowFuncEnter();
65
66 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
67 AssertPtr(pCtx);
68
69 pCtx->pEnv = pEnv;
70 pCtx->hModHook = NIL_RTLDRMOD;
71
72 OSVERSIONINFO OSinfo;
73 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
74 GetVersionEx (&OSinfo);
75
76 int rc;
77
78 /* We have to jump out here when using NT4, otherwise it complains about
79 a missing API function "UnhookWinEvent" used by the dynamically loaded VBoxHook.dll below */
80 if (OSinfo.dwMajorVersion <= 4) /* Windows NT 4.0 or older */
81 {
82 Log(("VBoxTray: VBoxSeamlessInit: Windows NT 4.0 or older not supported!\n"));
83 rc = VERR_NOT_SUPPORTED;
84 }
85 else
86 {
87 /* Will fail if SetWinEventHook is not present (version < NT4 SP6 apparently) */
88 rc = RTLdrLoadAppPriv(VBOXHOOK_DLL_NAME, &pCtx->hModHook);
89 if (RT_SUCCESS(rc))
90 {
91 *(PFNRT *)&pCtx->pfnVBoxHookInstallWindowTracker = RTLdrGetFunction(pCtx->hModHook, "VBoxHookInstallWindowTracker");
92 *(PFNRT *)&pCtx->pfnVBoxHookRemoveWindowTracker = RTLdrGetFunction(pCtx->hModHook, "VBoxHookRemoveWindowTracker");
93
94 /* rc should contain success status */
95 AssertRC(rc); /** @todo r=andy Makes no sense here!? */
96
97 VBoxSeamlessSetSupported(TRUE);
98
99 *ppInstance = pCtx;
100 }
101 else
102 LogFlowFunc(("Unable to load %s, rc=%Rrc\n", VBOXHOOK_DLL_NAME, rc));
103 }
104
105 LogFlowFuncLeaveRC(rc);
106 return rc;
107}
108
109void VBoxSeamlessDestroy(void *pInstance)
110{
111 LogFlowFuncEnter();
112
113 PVBOXSEAMLESSCONTEXT pCtx = (PVBOXSEAMLESSCONTEXT)pInstance;
114 AssertPtr(pCtx);
115
116 VBoxSeamlessSetSupported(FALSE);
117
118 /* Inform the host that we no longer support the seamless window mode. */
119 if (pCtx->pfnVBoxHookRemoveWindowTracker)
120 pCtx->pfnVBoxHookRemoveWindowTracker();
121 if (pCtx->hModHook != NIL_RTLDRMOD)
122 {
123 RTLdrClose(pCtx->hModHook);
124 pCtx->hModHook = NIL_RTLDRMOD;
125 }
126 return;
127}
128
129static void VBoxSeamlessInstallHook(void)
130{
131 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
132 AssertPtr(pCtx);
133
134 if (pCtx->pfnVBoxHookInstallWindowTracker)
135 {
136 /* Check current visible region state */
137 VBoxSeamlessCheckWindows(true);
138
139 HMODULE hMod = (HMODULE)RTLdrGetNativeHandle(pCtx->hModHook);
140 Assert(hMod != (HMODULE)~(uintptr_t)0);
141 pCtx->pfnVBoxHookInstallWindowTracker(hMod);
142 }
143}
144
145static void VBoxSeamlessRemoveHook(void)
146{
147 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
148 AssertPtr(pCtx);
149
150 if (pCtx->pfnVBoxHookRemoveWindowTracker)
151 pCtx->pfnVBoxHookRemoveWindowTracker();
152
153 if (pCtx->lpEscapeData)
154 {
155 free(pCtx->lpEscapeData);
156 pCtx->lpEscapeData = NULL;
157 }
158}
159
160extern HANDLE g_hSeamlessKmNotifyEvent;
161
162static VBOXDISPIF_SEAMLESS gVBoxDispIfSeamless; /** @todo r=andy Move this into VBOXSEAMLESSCONTEXT? */
163
164
165void VBoxSeamlessEnable(void)
166{
167 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
168 AssertPtr(pCtx);
169
170 Assert(g_hSeamlessKmNotifyEvent);
171
172 VBoxDispIfSeamlessCreate(&pCtx->pEnv->dispIf, &gVBoxDispIfSeamless, g_hSeamlessKmNotifyEvent);
173
174 VBoxSeamlessInstallHook();
175}
176
177void VBoxSeamlessDisable(void)
178{
179 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
180 AssertPtr(pCtx);
181 NOREF(pCtx);
182
183 VBoxSeamlessRemoveHook();
184
185 VBoxDispIfSeamlessTerm(&gVBoxDispIfSeamless);
186}
187
188BOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam)
189{
190 PVBOX_ENUM_PARAM lpParam = (PVBOX_ENUM_PARAM)lParam;
191 DWORD dwStyle, dwExStyle;
192 RECT rectWindow, rectVisible;
193
194 dwStyle = GetWindowLong(hwnd, GWL_STYLE);
195 dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
196 if ( !(dwStyle & WS_VISIBLE)
197 || (dwStyle & WS_CHILD))
198 return TRUE;
199
200 Log(("VBoxTray: VBoxEnumFunc %x\n", hwnd));
201 /* Only visible windows that are present on the desktop are interesting here */
202 if (GetWindowRect(hwnd, &rectWindow))
203 {
204 char szWindowText[256];
205 szWindowText[0] = 0;
206 OSVERSIONINFO OSinfo;
207 HWND hStart = NULL;
208 GetWindowText(hwnd, szWindowText, sizeof(szWindowText));
209 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
210 GetVersionEx (&OSinfo);
211 if (OSinfo.dwMajorVersion >= 6)
212 {
213 hStart = ::FindWindowEx(GetDesktopWindow(), NULL, "Button", "Start");
214 if ( hwnd == hStart && szWindowText != NULL
215 && !(strcmp(szWindowText, "Start"))
216 )
217 {
218 /* for vista and above. To solve the issue of small bar above
219 * the Start button when mouse is hovered over the start button in seamless mode.
220 * Difference of 7 is observed in Win 7 platform between the dimensionsof rectangle with Start title and its shadow.
221 */
222 rectWindow.top += 7;
223 rectWindow.bottom -=7;
224 }
225 }
226 rectVisible = rectWindow;
227
228#ifdef LOG_ENABLED
229 DWORD pid = 0;
230 DWORD tid = GetWindowThreadProcessId(hwnd, &pid);
231#endif
232
233 /* Filter out Windows XP shadow windows */
234 /** @todo still shows inside the guest */
235 if ( szWindowText[0] == 0
236 && (
237 (dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS)
238 && dwExStyle == (WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_TRANSPARENT|WS_EX_TOPMOST))
239 || (dwStyle == (WS_POPUP|WS_VISIBLE|WS_DISABLED|WS_CLIPSIBLINGS|WS_CLIPCHILDREN)
240 && dwExStyle == (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE))
241 || (dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN)
242 && dwExStyle == (WS_EX_TOOLWINDOW))
243 ))
244 {
245 Log(("VBoxTray: Filter out shadow window style=%x exstyle=%x\n", dwStyle, dwExStyle));
246 Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d) (filtered)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
247 Log(("VBoxTray: title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
248 Log(("VBoxTray: pid=%d tid=%d\n", pid, tid));
249 return TRUE;
250 }
251
252 /** @todo will this suffice? The Program Manager window covers the whole screen */
253 if (strcmp(szWindowText, "Program Manager"))
254 {
255 Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d) (applying)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
256 Log(("VBoxTray: title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
257 Log(("VBoxTray: pid=%d tid=%d\n", pid, tid));
258
259 HRGN hrgn = CreateRectRgn(0,0,0,0);
260
261 int ret = GetWindowRgn(hwnd, hrgn);
262
263 if (ret == ERROR)
264 {
265 Log(("VBoxTray: GetWindowRgn failed with rc=%d\n", GetLastError()));
266 SetRectRgn(hrgn, rectVisible.left, rectVisible.top, rectVisible.right, rectVisible.bottom);
267 }
268 else
269 {
270 /* this region is relative to the window origin instead of the desktop origin */
271 OffsetRgn(hrgn, rectWindow.left, rectWindow.top);
272 }
273 if (lpParam->hrgn)
274 {
275 /* create a union of the current visible region and the visible rectangle of this window. */
276 CombineRgn(lpParam->hrgn, lpParam->hrgn, hrgn, RGN_OR);
277 DeleteObject(hrgn);
278 }
279 else
280 lpParam->hrgn = hrgn;
281 }
282 else
283 {
284 Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d) (ignored)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
285 Log(("VBoxTray: title=%s style=%x\n", szWindowText, dwStyle));
286 Log(("VBoxTray: pid=%d tid=%d\n", pid, tid));
287 }
288 }
289 return TRUE; /* continue enumeration */
290}
291
292void VBoxSeamlessCheckWindows(bool fForce)
293{
294 PVBOXSEAMLESSCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
295 AssertPtr(pCtx);
296
297 if (!VBoxDispIfSeamlesIsValid(&gVBoxDispIfSeamless))
298 return;
299
300 VBOX_ENUM_PARAM param;
301
302 param.hdc = GetDC(HWND_DESKTOP);
303 param.hrgn = 0;
304
305 EnumWindows(VBoxEnumFunc, (LPARAM)&param);
306
307 if (param.hrgn)
308 {
309 DWORD cbSize;
310
311 cbSize = GetRegionData(param.hrgn, 0, NULL);
312 if (cbSize)
313 {
314 PVBOXDISPIFESCAPE lpEscapeData = (PVBOXDISPIFESCAPE)malloc(VBOXDISPIFESCAPE_SIZE(cbSize));
315 if (lpEscapeData)
316 {
317 lpEscapeData->escapeCode = VBOXESC_SETVISIBLEREGION;
318 LPRGNDATA lpRgnData = VBOXDISPIFESCAPE_DATA(lpEscapeData, RGNDATA);
319 memset(lpRgnData, 0, cbSize);
320 cbSize = GetRegionData(param.hrgn, cbSize, lpRgnData);
321 if (cbSize)
322 {
323#ifdef DEBUG
324 RECT *lpRect = (RECT *)&lpRgnData->Buffer[0];
325 Log(("VBoxTray: New visible region: \n"));
326
327 for (DWORD i=0;i<lpRgnData->rdh.nCount;i++)
328 {
329 Log(("VBoxTray: visible rect (%d,%d)(%d,%d)\n", lpRect[i].left, lpRect[i].top, lpRect[i].right, lpRect[i].bottom));
330 }
331#endif
332 LPRGNDATA lpCtxRgnData = VBOXDISPIFESCAPE_DATA(pCtx->lpEscapeData, RGNDATA);
333 if (fForce
334 || !pCtx->lpEscapeData
335 || (lpCtxRgnData->rdh.dwSize + lpCtxRgnData->rdh.nRgnSize != cbSize)
336 || memcmp(lpCtxRgnData, lpRgnData, cbSize))
337 {
338 /* send to display driver */
339 VBoxDispIfSeamlessSubmit(&gVBoxDispIfSeamless, lpEscapeData, cbSize);
340
341 if (pCtx->lpEscapeData)
342 free(pCtx->lpEscapeData);
343 pCtx->lpEscapeData = lpEscapeData;
344 }
345 else
346 Log(("VBoxTray: Visible rectangles haven't changed; ignore\n"));
347 }
348 if (lpEscapeData != pCtx->lpEscapeData)
349 free(lpEscapeData);
350 }
351 }
352
353 DeleteObject(param.hrgn);
354 }
355
356 ReleaseDC(HWND_DESKTOP, param.hdc);
357}
358
359/**
360 * Thread function to wait for and process seamless mode change
361 * requests
362 */
363static DECLCALLBACK(int) VBoxSeamlessWorker(void *pInstance, bool volatile *pfShutdown)
364{
365 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
366 LogFlowFunc(("pInstance=%p\n", pInstance));
367
368 /*
369 * Tell the control thread that it can continue
370 * spawning services.
371 */
372 RTThreadUserSignal(RTThreadSelf());
373
374 PVBOXSEAMLESSCONTEXT pCtx = (PVBOXSEAMLESSCONTEXT)pInstance;
375
376 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
377 VBoxGuestFilterMaskInfo maskInfo;
378 DWORD cbReturned;
379 BOOL fWasScreenSaverActive = FALSE, fRet;
380
381 maskInfo.u32OrMask = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
382 maskInfo.u32NotMask = 0;
383 if (!DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
384 {
385 DWORD dwErr = GetLastError();
386 LogRel(("Seamless: DeviceIOControl(CtlMask) failed with %ld, exiting ...\n", dwErr));
387 return RTErrConvertFromWin32(dwErr);
388 }
389
390 int rc = VINF_SUCCESS;
391
392 for (;;)
393 {
394 /* wait for a seamless change event */
395 VBoxGuestWaitEventInfo waitEvent;
396 waitEvent.u32TimeoutIn = 5000;
397 waitEvent.u32EventMaskIn = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
398 if (DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
399 {
400 /* are we supposed to stop? */
401 if (*pfShutdown)
402 break;
403
404 /* did we get the right event? */
405 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST)
406 {
407 /* We got at least one event. Read the requested resolution
408 * and try to set it until success. New events will not be seen
409 * but a new resolution will be read in this poll loop.
410 */
411 for (;;)
412 {
413 /* get the seamless change request */
414 VMMDevSeamlessChangeRequest seamlessChangeRequest = {0};
415 vmmdevInitRequest((VMMDevRequestHeader*)&seamlessChangeRequest, VMMDevReq_GetSeamlessChangeRequest);
416 seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
417
418 BOOL fSeamlessChangeQueried = DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(seamlessChangeRequest)), &seamlessChangeRequest, sizeof(seamlessChangeRequest),
419 &seamlessChangeRequest, sizeof(seamlessChangeRequest), &cbReturned, NULL);
420 if (fSeamlessChangeQueried)
421 {
422 LogFlowFunc(("Mode changed to %d\n", seamlessChangeRequest.mode));
423
424 switch(seamlessChangeRequest.mode)
425 {
426 case VMMDev_Seamless_Disabled:
427 if (fWasScreenSaverActive)
428 {
429 LogRel(("Seamless: Re-enabling the screensaver\n"));
430 fRet = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0);
431 if (!fRet)
432 LogRel(("Seamless: SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %ld\n", GetLastError()));
433 }
434 PostMessage(g_hwndToolWindow, WM_VBOX_SEAMLESS_DISABLE, 0, 0);
435 break;
436
437 case VMMDev_Seamless_Visible_Region:
438 fRet = SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fWasScreenSaverActive, 0);
439 if (!fRet)
440 LogRel(("Seamless: SystemParametersInfo SPI_GETSCREENSAVEACTIVE failed with %ld\n", GetLastError()));
441
442 if (fWasScreenSaverActive)
443 LogRel(("Seamless: Disabling the screensaver\n"));
444
445 fRet = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
446 if (!fRet)
447 LogRel(("Seamless: SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %ld\n", GetLastError()));
448 PostMessage(g_hwndToolWindow, WM_VBOX_SEAMLESS_ENABLE, 0, 0);
449 break;
450
451 case VMMDev_Seamless_Host_Window:
452 break;
453
454 default:
455 AssertFailed();
456 break;
457 }
458 break;
459 }
460 else
461 LogRel(("Seamless: DeviceIoControl(ChangeReq) failed with %ld\n", GetLastError()));
462
463 if (*pfShutdown)
464 break;
465
466 /* sleep a bit to not eat too much CPU while retrying */
467 RTThreadSleep(10);
468 }
469 }
470 }
471 else
472 {
473 /* sleep a bit to not eat too much CPU in case the above call always fails */
474 RTThreadSleep(10);
475 }
476
477 if (*pfShutdown)
478 break;
479 }
480
481 maskInfo.u32OrMask = 0;
482 maskInfo.u32NotMask = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
483 if (!DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
484 LogRel(("Seamless: DeviceIOControl(CtlMask) failed with %ld\n", GetLastError()));
485
486 LogFlowFuncLeaveRC(rc);
487 return rc;
488}
489
490/**
491 * The service description.
492 */
493VBOXSERVICEDESC g_SvcDescSeamless =
494{
495 /* pszName. */
496 "seamless",
497 /* pszDescription. */
498 "Seamless Windows",
499 /* methods */
500 VBoxSeamlessInit,
501 VBoxSeamlessWorker,
502 NULL /* pfnStop */,
503 VBoxSeamlessDestroy
504};
505
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