VirtualBox

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

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

updates

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