VirtualBox

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

Last change on this file since 62556 was 62522, checked in by vboxsync, 8 years ago

(C) 2016

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