VirtualBox

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

Last change on this file since 58049 was 57741, checked in by vboxsync, 9 years ago

Additions/VBoxTray:

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