VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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