VirtualBox

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

Last change on this file since 29775 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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