VirtualBox

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

Last change on this file since 51556 was 51469, checked in by vboxsync, 11 years ago

VBoxTray: Logging; ripped out all custom logging.

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