VirtualBox

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

Last change on this file since 69496 was 69496, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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